[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-aws-labs-edc-data-validation":3,"mdc--m43a2o-key":50,"related-org-aws-labs-edc-data-validation":2030,"related-repo-aws-labs-edc-data-validation":2212},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":26,"repoUrl":27,"updatedAt":28,"license":29,"forks":30,"topics":31,"repo":45,"sourceUrl":48,"mdContent":49},"edc-data-validation","validate clinical data exports","Generate code for EDC export validation, clinical data range checks, cross-form consistency checks, SDTM structure validation, controlled terminology verification, and define.xml generation. Use when the user asks to validate clinical trial data exports, check vital sign or lab value ranges, verify AE date consistency, validate SDTM datasets against CDISC rules, generate define.xml, or build an automated data review pipeline. Triggers include \"EDC validation\", \"range check clinical data\", \"cross-form consistency\", \"SDTM validation\", \"controlled terminology check\", \"define.xml generation\", \"Medidata Rave export\", \"Oracle InForm\", \"Veeva Vault CDMS\", \"clinical data cleaning\", \"edit check\", \"data query\", \"SDTM structure check\", \"lab range check\", \"vital signs validation\", \"AE date check\", \"protocol deviation detection\", \"OpenCDISC\", \"Pinnacles 21\", \"P21 validation\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"aws-labs","AWS Labs","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Faws-labs.png","awslabs",[13,17,20,23],{"name":14,"slug":15,"type":16},"Data Quality","data-quality","tag",{"name":18,"slug":19,"type":16},"Healthcare","healthcare",{"name":21,"slug":22,"type":16},"Audit","audit",{"name":24,"slug":25,"type":16},"Clinical Trials","clinical-trials",4,"https:\u002F\u002Fgithub.com\u002Fawslabs\u002Fhcls-agent-skills","2026-07-12T08:37:58.940653",null,0,[32,33,34,35,36,37,38,39,40,41,42,43,44],"agent-skills","agentcore","ai-agents","amazon-quick-desktop","claims-processing","drug-discovery","genomics","healthcare-ai","kiro","life-sciences","medical-imaging","risk-adjustment","strands-agents",{"repoUrl":27,"stars":26,"forks":30,"topics":46,"description":47},[32,33,34,35,36,37,38,39,40,41,42,43,44],"Agent skills for healthcare and life sciences: genomics, imaging, claims, drug discovery, and more. Works with Amazon Quick, Kiro, Amazon AgentCore, AWS Strands SDK, Claude Code, Codex, and any Agent Skills-compatible platform.","https:\u002F\u002Fgithub.com\u002Fawslabs\u002Fhcls-agent-skills\u002Ftree\u002FHEAD\u002Fskills\u002Fedc-data-validation","---\nname: edc-data-validation\ndescription: >\n  Generate code for EDC export validation, clinical data range checks, cross-form\n  consistency checks, SDTM structure validation, controlled terminology verification,\n  and define.xml generation. Use when the user asks to validate clinical trial data\n  exports, check vital sign or lab value ranges, verify AE date consistency, validate\n  SDTM datasets against CDISC rules, generate define.xml, or build an automated data\n  review pipeline. Triggers include \"EDC validation\", \"range check clinical data\",\n  \"cross-form consistency\", \"SDTM validation\", \"controlled terminology check\",\n  \"define.xml generation\", \"Medidata Rave export\", \"Oracle InForm\", \"Veeva Vault CDMS\",\n  \"clinical data cleaning\", \"edit check\", \"data query\", \"SDTM structure check\",\n  \"lab range check\", \"vital signs validation\", \"AE date check\", \"protocol deviation\n  detection\", \"OpenCDISC\", \"Pinnacles 21\", \"P21 validation\".\nusage: Invoke to generate Python or R code for clinical data validation and SDTM compliance checks.\nversion: 1.0.0\nvalidated_against:\n  date: 2025-01-15\n  packages: {pinnacle21: \"4.0\", define-xml: \"2.1\"}\ntags: [skill, category:pipeline, clinical-data, edc, sdtm, cdisc, validation, define-xml, hcls]\n---\n\n# EDC Data Validation — Pipeline Skill\n\n## Overview\n\nYou generate deterministic Python code for clinical data validation pipelines.\nWhen the user provides EDC exports or SDTM datasets, produce validation code following\nthe recipes below. Always confirm the input format and therapeutic area before generating.\n\n## Usage\n\n- Validate EDC exports with range checks, cross-form consistency, and partial date imputation\n- Check SDTM datasets against CDISC controlled terminology and structure rules\n- Generate define.xml and prioritized validation reports\n\n## Core Concepts\n\n---\n\n## Response Format\n\n- Lead with the command or code the user needs — explain after\n- Structure as: confirm inputs → working code → key parameters explained → gotchas\n- One complete working example per task; do not show every alternative\n- Keep code comments minimal and functional (what, not why-it-exists)\n- Target: 50-100 lines of code with brief surrounding explanation\n\n---\n\n## 1. Validation Priority Decision Tree\n\nUse this to triage findings and assign query priority:\n\n```\nIssue Detected\n├── Missing\u002Finvalid RFSTDTC or DSSTDTC? → CRITICAL (blocks submission)\n├── AE date outside study period (before consent or after completion)? → CRITICAL\n├── CT violation on required codelist (SEX, AEOUT, AESER)? → CRITICAL\n├── Out-of-range vital sign WITHOUT data comment? → HIGH\n├── Missing AESER when AEOUT='FATAL' or SAE reported? → HIGH\n├── Lab value triggers Hy's Law flag (ALT\u002FAST >3×ULN + BILI >2×ULN)? → HIGH\n├── Minor CT mismatch (case difference, trailing space)? → MEDIUM\n├── Missing optional variable (AEENDTC, AESEV)? → MEDIUM\n├── Formatting issue (date separators, decimal precision)? → LOW\n└── Case sensitivity only (e.g., \"Male\" vs \"M\")? → LOW\n```\n\n**Severity → Action mapping:**\n- CRITICAL → Auto-query, blocks dataset lock\n- HIGH → Query within 48h, reviewer will flag\n- MEDIUM → Batch query at next data cut\n- LOW → Note for final clean-up, no query needed\n\n---\n\n## 2. Therapeutic-Area Decision Tree\n\nSelect range tables based on study type:\n\n```\nWhat is the therapeutic area?\n├── Oncology\n│   ├── Use RECIST response values: CR, PR, SD, PD, NE\n│   ├── Tumor measurement: 0–300 mm (flag >200 as HIGH)\n│   └── ECOG valid values: 0, 1, 2, 3, 4, 5\n├── Cardiology\n│   ├── QTcF: normal \u003C450ms, borderline 450–480ms, prolonged >480ms, critical >500ms\n│   ├── SYSBP: 70–200 mmHg (tighter than general)\n│   └── HR: 40–150 beats\u002Fmin (tighter than general)\n├── Pediatric (age \u003C18)\n│   ├── SYSBP: multiply adult low by 0.7, high by 0.8\n│   ├── HR: multiply adult high by 1.3 (higher resting HR)\n│   ├── WEIGHT: 2–120 kg\n│   └── HEIGHT: 30–200 cm\n└── General \u002F Geriatric (age ≥65)\n    ├── Use standard ranges (§3 below)\n    └── Flag HR >180 as CRITICAL (not just HIGH)\n```\n\n---\n\n## 3. Range Checks — Vital Signs and Labs\n\n```python\nimport pandas as pd\n\nVS_RANGES = {\n    \"SYSBP\":  {\"low\": 60, \"high\": 250, \"unit\": \"mmHg\"},\n    \"DIABP\":  {\"low\": 30, \"high\": 150, \"unit\": \"mmHg\"},\n    \"HR\":     {\"low\": 30, \"high\": 220, \"unit\": \"beats\u002Fmin\"},\n    \"TEMP\":   {\"low\": 34.0, \"high\": 42.0, \"unit\": \"C\"},\n    \"WEIGHT\": {\"low\": 20, \"high\": 300, \"unit\": \"kg\"},\n    \"HEIGHT\": {\"low\": 50, \"high\": 250, \"unit\": \"cm\"},\n    \"RESP\":   {\"low\": 6, \"high\": 60, \"unit\": \"breaths\u002Fmin\"},\n}\n\nLB_RANGES = {\n    \"ALT\":   {\"low\": 0, \"high\": 500, \"unit\": \"U\u002FL\"},\n    \"AST\":   {\"low\": 0, \"high\": 500, \"unit\": \"U\u002FL\"},\n    \"BILI\":  {\"low\": 0, \"high\": 30, \"unit\": \"mg\u002FdL\"},\n    \"CREAT\": {\"low\": 0, \"high\": 20, \"unit\": \"mg\u002FdL\"},\n    \"HGB\":   {\"low\": 3, \"high\": 25, \"unit\": \"g\u002FdL\"},\n    \"WBC\":   {\"low\": 0, \"high\": 100, \"unit\": \"10^9\u002FL\"},\n    \"PLAT\":  {\"low\": 0, \"high\": 1500, \"unit\": \"10^9\u002FL\"},\n    \"GLUC\":  {\"low\": 10, \"high\": 1000, \"unit\": \"mg\u002FdL\"},\n}\n\n# Pediatric multipliers (apply to VS_RANGES when age \u003C 18)\nPEDIATRIC_MULT = {\"SYSBP_low\": 0.7, \"SYSBP_high\": 0.8, \"HR_high\": 1.3}\n\n# Hy's Law: ALT or AST >3×ULN AND BILI >2×ULN (ULN: ALT=40, AST=37, BILI=1.2)\nHYS_LAW = {\"ALT_ULN\": 40, \"AST_ULN\": 37, \"BILI_ULN\": 1.2, \"ALT_MULT\": 3, \"BILI_MULT\": 2}\n\ndef check_ranges(df: pd.DataFrame, testcd_col: str, value_col: str,\n                 ranges: dict, domain: str) -> pd.DataFrame:\n    issues = []\n    for testcd, limits in ranges.items():\n        subset = df[df[testcd_col] == testcd]\n        oob = subset[(subset[value_col] \u003C limits[\"low\"]) | (subset[value_col] > limits[\"high\"])]\n        for _, row in oob.iterrows():\n            severity = \"HIGH\" if testcd in (\"SYSBP\", \"HR\", \"ALT\", \"AST\", \"BILI\") else \"MEDIUM\"\n            issues.append({\"USUBJID\": row.get(\"USUBJID\"), \"DOMAIN\": domain,\n                           \"TESTCD\": testcd, \"VALUE\": row[value_col],\n                           \"EXPECTED\": f\"{limits['low']}–{limits['high']} {limits['unit']}\",\n                           \"SEVERITY\": severity})\n    return pd.DataFrame(issues)\n```\n\n---\n\n## 4. Cross-Form Consistency Checks (8 checks)\n\n```python\ndef cross_form_checks(datasets: dict[str, pd.DataFrame]) -> pd.DataFrame:\n    ae, ds, dm = datasets.get(\"AE\", pd.DataFrame()), datasets.get(\"DS\", pd.DataFrame()), datasets.get(\"DM\", pd.DataFrame())\n    sv, cm, ex = datasets.get(\"SV\", pd.DataFrame()), datasets.get(\"CM\", pd.DataFrame()), datasets.get(\"EX\", pd.DataFrame())\n    issues = []\n    to_dt = lambda s: pd.to_datetime(s, errors=\"coerce\")\n\n    # 1. AE start \u003C informed consent → CRITICAL\n    if not ae.empty and \"RFICDTC\" in dm.columns:\n        ic = dm.set_index(\"USUBJID\")[\"RFICDTC\"].apply(to_dt)\n        for _, r in ae.assign(AESTDTC=to_dt(ae[\"AESTDTC\"])).iterrows():\n            ic_dt = ic.get(r[\"USUBJID\"])\n            if pd.notna(ic_dt) and pd.notna(r[\"AESTDTC\"]) and r[\"AESTDTC\"] \u003C ic_dt:\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"AE_BEFORE_CONSENT\", \"SEVERITY\": \"CRITICAL\"})\n\n    # 2. Fatal AE without DS death record → CRITICAL\n    if not ae.empty and not ds.empty and \"DSDECOD\" in ds.columns:\n        death_subj = set(ds[ds[\"DSDECOD\"] == \"DEATH\"][\"USUBJID\"])\n        for _, r in ae[ae[\"AEOUT\"] == \"FATAL\"].iterrows():\n            if r[\"USUBJID\"] not in death_subj:\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"FATAL_AE_NO_DEATH\", \"SEVERITY\": \"CRITICAL\"})\n\n    # 3. Disposition date > last visit + 7 days → HIGH\n    if not ds.empty and not sv.empty:\n        last_sv = sv.groupby(\"USUBJID\")[\"SVSTDTC\"].max().apply(to_dt)\n        for _, r in ds.assign(DSSTDTC=to_dt(ds[\"DSSTDTC\"])).iterrows():\n            lv = last_sv.get(r[\"USUBJID\"])\n            if pd.notna(lv) and pd.notna(r[\"DSSTDTC\"]) and r[\"DSSTDTC\"] > lv + pd.Timedelta(days=7):\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"DS_AFTER_LAST_VISIT\", \"SEVERITY\": \"HIGH\"})\n\n    # 4. Lab collection on non-visit day → MEDIUM\n    if not datasets.get(\"LB\", pd.DataFrame()).empty and not sv.empty:\n        sv_set = set(zip(sv[\"USUBJID\"], to_dt(sv[\"SVSTDTC\"]).dt.date))\n        lb = datasets[\"LB\"].assign(LBDTC=to_dt(datasets[\"LB\"].get(\"LBDTC\")))\n        for _, r in lb.dropna(subset=[\"LBDTC\"]).iterrows():\n            if (r[\"USUBJID\"], r[\"LBDTC\"].date()) not in sv_set:\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"LB_NO_VISIT\", \"SEVERITY\": \"MEDIUM\"})\n\n    # 5. Conmed start after study completion → MEDIUM\n    if not cm.empty and \"RFPENDTC\" in dm.columns:\n        end = dm.set_index(\"USUBJID\")[\"RFPENDTC\"].apply(to_dt)\n        for _, r in cm.assign(CMSTDTC=to_dt(cm.get(\"CMSTDTC\"))).dropna(subset=[\"CMSTDTC\"]).iterrows():\n            e = end.get(r[\"USUBJID\"])\n            if pd.notna(e) and r[\"CMSTDTC\"] > e:\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"CM_AFTER_COMPLETION\", \"SEVERITY\": \"MEDIUM\"})\n\n    # 6. Randomization without signed consent → CRITICAL\n    if not dm.empty and \"RFICDTC\" in dm.columns and \"ARMCD\" in dm.columns:\n        for _, r in dm.iterrows():\n            if pd.notna(r.get(\"ARMCD\")) and r[\"ARMCD\"] != \"SCRNFAIL\" and pd.isna(r.get(\"RFICDTC\")):\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"RAND_NO_CONSENT\", \"SEVERITY\": \"CRITICAL\"})\n\n    # 7. Unscheduled visit without comment → MEDIUM\n    if not sv.empty and \"SVUPDES\" in sv.columns:\n        unsched = sv[sv[\"VISITNUM\"].astype(str).str.contains(r\"\\.\", na=False)]\n        no_comment = unsched[unsched[\"SVUPDES\"].isna() | (unsched[\"SVUPDES\"].str.strip() == \"\")]\n        for _, r in no_comment.iterrows():\n            issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"UNSCHED_NO_COMMENT\", \"SEVERITY\": \"MEDIUM\"})\n\n    # 8. Dose modification without corresponding AE → HIGH\n    if not ex.empty and not ae.empty and \"EXADJ\" in ex.columns:\n        ae_subj = set(zip(ae[\"USUBJID\"], to_dt(ae.get(\"AESTDTC\")).dt.date))\n        dose_mod = ex[ex[\"EXADJ\"].notna() & (ex[\"EXADJ\"] != \"\")].assign(EXSTDTC=to_dt(ex[\"EXSTDTC\"]))\n        for _, r in dose_mod.dropna(subset=[\"EXSTDTC\"]).iterrows():\n            if (r[\"USUBJID\"], r[\"EXSTDTC\"].date()) not in ae_subj:\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"DOSE_MOD_NO_AE\", \"SEVERITY\": \"HIGH\"})\n\n    return pd.DataFrame(issues)\n```\n\n---\n\n## 5. Partial Date Imputation\n\n```python\nimport calendar\n\ndef impute_partial_date(dtc: str, position: str = \"start\") -> tuple[str, str]:\n    \"\"\"Impute partial ISO 8601 dates. Returns (imputed_date, DTYPE).\n    position: 'start' imputes early, 'end' imputes late.\"\"\"\n    if pd.isna(dtc) or dtc.strip() == \"\":\n        return (None, None)\n    parts = dtc.split(\"-\")\n    if len(parts) == 3 and len(parts[2]) >= 2:\n        return (dtc, None)  # Complete date, no imputation\n    year = int(parts[0])\n    if len(parts) == 1:  # YYYY only\n        if position == \"start\":\n            return (f\"{year}-01-01\", \"FIRST\")\n        return (f\"{year}-12-31\", \"LAST\")\n    month = int(parts[1])\n    if len(parts) == 2:  # YYYY-MM only\n        if position == \"start\":\n            return (f\"{year}-{month:02d}-01\", \"FIRST\")\n        last_day = calendar.monthrange(year, month)[1]\n        return (f\"{year}-{month:02d}-{last_day}\", \"LAST\")\n    return (dtc, None)\n```\n\n---\n\n## 6. Aggregate Report and define.xml\n\n```python\nfrom lxml import etree\n\ndef generate_validation_report(*issue_dfs, output_path=\"validation_report.csv\"):\n    \"\"\"Combine all issues into a single prioritized report.\"\"\"\n    all_issues = pd.concat([df for df in issue_dfs if not df.empty], ignore_index=True)\n    sev_order = {\"CRITICAL\": 0, \"HIGH\": 1, \"MEDIUM\": 2, \"LOW\": 3}\n    all_issues = all_issues.sort_values(by=\"SEVERITY\", key=lambda s: s.map(sev_order).fillna(4))\n    all_issues.to_csv(output_path, index=False)\n    for sev in [\"CRITICAL\", \"HIGH\", \"MEDIUM\", \"LOW\"]:\n        print(f\"  {sev}: {(all_issues['SEVERITY'] == sev).sum()}\")\n    return all_issues\n\ndef generate_define_xml(datasets: dict[str, pd.DataFrame], study_oid: str,\n                        output_path: str = \"define.xml\") -> None:\n    \"\"\"Generate minimal define.xml 2.0 skeleton from SDTM datasets.\"\"\"\n    NS = \"http:\u002F\u002Fwww.cdisc.org\u002Fns\u002Fodm\u002Fv1.3\"\n    DEF = \"http:\u002F\u002Fwww.cdisc.org\u002Fns\u002Fdef\u002Fv2.0\"\n    nsmap = {None: NS, \"def\": DEF, \"xlink\": \"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxlink\"}\n    root = etree.Element(\"ODM\", nsmap=nsmap, FileOID=f\"define_{study_oid}\",\n                          FileType=\"Snapshot\", ODMVersion=\"1.3.2\")\n    study = etree.SubElement(root, \"Study\", OID=study_oid)\n    gd = etree.SubElement(study, \"GlobalVariables\")\n    etree.SubElement(gd, \"StudyName\").text = study_oid\n    etree.SubElement(gd, \"ProtocolName\").text = study_oid\n    mdv = etree.SubElement(study, \"MetaDataVersion\", OID=\"MDV.1\", Name=\"SDTM Metadata\")\n    for domain_name, df in sorted(datasets.items()):\n        igd = etree.SubElement(mdv, \"ItemGroupDef\", OID=f\"IG.{domain_name}\",\n                               Name=domain_name, Repeating=\"Yes\" if domain_name != \"DM\" else \"No\")\n        for col in df.columns:\n            dtype = \"integer\" if df[col].dtype in (\"int64\", \"float64\") else \"text\"\n            etree.SubElement(mdv, \"ItemDef\", OID=f\"IT.{domain_name}.{col}\",\n                             Name=col, DataType=dtype)\n            etree.SubElement(igd, \"ItemRef\", ItemOID=f\"IT.{domain_name}.{col}\",\n                             Mandatory=\"Yes\" if col in (\"STUDYID\", \"USUBJID\") else \"No\")\n    etree.ElementTree(root).write(output_path, xml_declaration=True,\n                                   encoding=\"UTF-8\", pretty_print=True)\n```\n\n---\n\n## 7. Parameter Reference\n\n| Parameter | Default | Notes |\n|---|---|---|\n| VS SYSBP range | 60–250 mmHg | Pediatric: ×0.7 low, ×0.8 high |\n| VS HR range | 30–220 beats\u002Fmin | Pediatric: ×1.3 high; Geriatric: flag >180 as CRITICAL |\n| LB ALT\u002FAST | 0–500 U\u002FL | Flag >3×ULN (ALT ULN=40, AST ULN=37) for Hy's Law |\n| LB BILI | 0–30 mg\u002FdL | Hy's Law: >2×ULN (ULN=1.2) with elevated ALT\u002FAST |\n| QTcF (cardiology) | \u003C450 normal | 450–480 borderline, >480 prolonged, >500 CRITICAL |\n| RECIST (oncology) | CR\u002FPR\u002FSD\u002FPD\u002FNE | Flag any other value as CT violation |\n| Partial date imputation | Start→earliest, End→latest | Always set DTYPE column |\n| Severity levels | CRITICAL\u002FHIGH\u002FMEDIUM\u002FLOW | Map to query priority P1–P4 |\n\n---\n\n## Common Mistakes\n\n- **Wrong:** Applying adult vital sign ranges to pediatric subjects without age adjustment\n  **Right:** Scale ranges by age group (e.g., pediatric SBP upper = adult × 0.8, HR upper = adult × 1.3)\n  **Why:** Normal pediatric values differ substantially from adults — flagging a child's HR of 130 as abnormal wastes data management time\n\n- **Wrong:** Flagging partial dates as errors instead of applying imputation rules\n  **Right:** Impute start dates to earliest possible (01-JAN-YYYY) and end dates to latest possible, then set DTYPE = \"DERIVED\"\n  **Why:** Partial dates are expected in clinical data; rejecting them loses valid records and violates SDTM imputation conventions\n\n- **Wrong:** Validating SDTM controlled terminology against a hardcoded list instead of the study's CT version\n  **Right:** Always validate against the specific CDISC CT version declared in the study's define.xml\n  **Why:** CT evolves across versions — a valid term in CT 2023-12-15 may not exist in CT 2022-09-30 and vice versa\n\n- **Wrong:** Running cross-form date checks without accounting for time zones or visit windows\n  **Right:** Allow ±1 day tolerance for cross-form date comparisons and document the tolerance in the validation plan\n  **Why:** Subjects crossing time zones or overnight visits produce legitimate 1-day discrepancies that are not data errors\n\n- **Wrong:** Generating queries for every out-of-range lab value without checking units\n  **Right:** Normalize units before range checking (e.g., convert mg\u002FdL ↔ µmol\u002FL for creatinine) using LBORRESU\u002FLBSTRESU\n  **Why:** A creatinine of 88 µmol\u002FL is normal but flags as critical if the range check assumes mg\u002FdL (normal: 0.6–1.2)\n\n- **Wrong:** Treating all validation findings as equal priority\n  **Right:** Classify by clinical impact: CRITICAL (patient safety), HIGH (primary endpoint), MEDIUM (secondary), LOW (cosmetic)\n  **Why:** Flooding sites with low-priority queries delays resolution of safety-critical issues\n",{"data":51,"body":69},{"name":4,"description":6,"usage":52,"version":53,"validated_against":54,"tags":59},"Invoke to generate Python or R code for clinical data validation and SDTM compliance checks.","1.0.0",{"date":55,"packages":56},"2025-01-15",{"pinnacle21":57,"define-xml":58},"4.0","2.1",[60,61,62,63,64,65,66,67,68],"skill","category:pipeline","clinical-data","edc","sdtm","cdisc","validation","define-xml","hcls",{"type":70,"children":71},"root",[72,81,88,94,100,120,126,130,136,164,167,173,178,191,200,223,226,232,237,246,249,255,639,642,648,1205,1208,1214,1395,1398,1404,1697,1700,1706,1883,1886,1892,2024],{"type":73,"tag":74,"props":75,"children":77},"element","h1",{"id":76},"edc-data-validation-pipeline-skill",[78],{"type":79,"value":80},"text","EDC Data Validation — Pipeline Skill",{"type":73,"tag":82,"props":83,"children":85},"h2",{"id":84},"overview",[86],{"type":79,"value":87},"Overview",{"type":73,"tag":89,"props":90,"children":91},"p",{},[92],{"type":79,"value":93},"You generate deterministic Python code for clinical data validation pipelines.\nWhen the user provides EDC exports or SDTM datasets, produce validation code following\nthe recipes below. Always confirm the input format and therapeutic area before generating.",{"type":73,"tag":82,"props":95,"children":97},{"id":96},"usage",[98],{"type":79,"value":99},"Usage",{"type":73,"tag":101,"props":102,"children":103},"ul",{},[104,110,115],{"type":73,"tag":105,"props":106,"children":107},"li",{},[108],{"type":79,"value":109},"Validate EDC exports with range checks, cross-form consistency, and partial date imputation",{"type":73,"tag":105,"props":111,"children":112},{},[113],{"type":79,"value":114},"Check SDTM datasets against CDISC controlled terminology and structure rules",{"type":73,"tag":105,"props":116,"children":117},{},[118],{"type":79,"value":119},"Generate define.xml and prioritized validation reports",{"type":73,"tag":82,"props":121,"children":123},{"id":122},"core-concepts",[124],{"type":79,"value":125},"Core Concepts",{"type":73,"tag":127,"props":128,"children":129},"hr",{},[],{"type":73,"tag":82,"props":131,"children":133},{"id":132},"response-format",[134],{"type":79,"value":135},"Response Format",{"type":73,"tag":101,"props":137,"children":138},{},[139,144,149,154,159],{"type":73,"tag":105,"props":140,"children":141},{},[142],{"type":79,"value":143},"Lead with the command or code the user needs — explain after",{"type":73,"tag":105,"props":145,"children":146},{},[147],{"type":79,"value":148},"Structure as: confirm inputs → working code → key parameters explained → gotchas",{"type":73,"tag":105,"props":150,"children":151},{},[152],{"type":79,"value":153},"One complete working example per task; do not show every alternative",{"type":73,"tag":105,"props":155,"children":156},{},[157],{"type":79,"value":158},"Keep code comments minimal and functional (what, not why-it-exists)",{"type":73,"tag":105,"props":160,"children":161},{},[162],{"type":79,"value":163},"Target: 50-100 lines of code with brief surrounding explanation",{"type":73,"tag":127,"props":165,"children":166},{},[],{"type":73,"tag":82,"props":168,"children":170},{"id":169},"_1-validation-priority-decision-tree",[171],{"type":79,"value":172},"1. Validation Priority Decision Tree",{"type":73,"tag":89,"props":174,"children":175},{},[176],{"type":79,"value":177},"Use this to triage findings and assign query priority:",{"type":73,"tag":179,"props":180,"children":184},"pre",{"className":181,"code":183,"language":79},[182],"language-text","Issue Detected\n├── Missing\u002Finvalid RFSTDTC or DSSTDTC? → CRITICAL (blocks submission)\n├── AE date outside study period (before consent or after completion)? → CRITICAL\n├── CT violation on required codelist (SEX, AEOUT, AESER)? → CRITICAL\n├── Out-of-range vital sign WITHOUT data comment? → HIGH\n├── Missing AESER when AEOUT='FATAL' or SAE reported? → HIGH\n├── Lab value triggers Hy's Law flag (ALT\u002FAST >3×ULN + BILI >2×ULN)? → HIGH\n├── Minor CT mismatch (case difference, trailing space)? → MEDIUM\n├── Missing optional variable (AEENDTC, AESEV)? → MEDIUM\n├── Formatting issue (date separators, decimal precision)? → LOW\n└── Case sensitivity only (e.g., \"Male\" vs \"M\")? → LOW\n",[185],{"type":73,"tag":186,"props":187,"children":189},"code",{"__ignoreMap":188},"",[190],{"type":79,"value":183},{"type":73,"tag":89,"props":192,"children":193},{},[194],{"type":73,"tag":195,"props":196,"children":197},"strong",{},[198],{"type":79,"value":199},"Severity → Action mapping:",{"type":73,"tag":101,"props":201,"children":202},{},[203,208,213,218],{"type":73,"tag":105,"props":204,"children":205},{},[206],{"type":79,"value":207},"CRITICAL → Auto-query, blocks dataset lock",{"type":73,"tag":105,"props":209,"children":210},{},[211],{"type":79,"value":212},"HIGH → Query within 48h, reviewer will flag",{"type":73,"tag":105,"props":214,"children":215},{},[216],{"type":79,"value":217},"MEDIUM → Batch query at next data cut",{"type":73,"tag":105,"props":219,"children":220},{},[221],{"type":79,"value":222},"LOW → Note for final clean-up, no query needed",{"type":73,"tag":127,"props":224,"children":225},{},[],{"type":73,"tag":82,"props":227,"children":229},{"id":228},"_2-therapeutic-area-decision-tree",[230],{"type":79,"value":231},"2. Therapeutic-Area Decision Tree",{"type":73,"tag":89,"props":233,"children":234},{},[235],{"type":79,"value":236},"Select range tables based on study type:",{"type":73,"tag":179,"props":238,"children":241},{"className":239,"code":240,"language":79},[182],"What is the therapeutic area?\n├── Oncology\n│   ├── Use RECIST response values: CR, PR, SD, PD, NE\n│   ├── Tumor measurement: 0–300 mm (flag >200 as HIGH)\n│   └── ECOG valid values: 0, 1, 2, 3, 4, 5\n├── Cardiology\n│   ├── QTcF: normal \u003C450ms, borderline 450–480ms, prolonged >480ms, critical >500ms\n│   ├── SYSBP: 70–200 mmHg (tighter than general)\n│   └── HR: 40–150 beats\u002Fmin (tighter than general)\n├── Pediatric (age \u003C18)\n│   ├── SYSBP: multiply adult low by 0.7, high by 0.8\n│   ├── HR: multiply adult high by 1.3 (higher resting HR)\n│   ├── WEIGHT: 2–120 kg\n│   └── HEIGHT: 30–200 cm\n└── General \u002F Geriatric (age ≥65)\n    ├── Use standard ranges (§3 below)\n    └── Flag HR >180 as CRITICAL (not just HIGH)\n",[242],{"type":73,"tag":186,"props":243,"children":244},{"__ignoreMap":188},[245],{"type":79,"value":240},{"type":73,"tag":127,"props":247,"children":248},{},[],{"type":73,"tag":82,"props":250,"children":252},{"id":251},"_3-range-checks-vital-signs-and-labs",[253],{"type":79,"value":254},"3. Range Checks — Vital Signs and Labs",{"type":73,"tag":179,"props":256,"children":260},{"className":257,"code":258,"language":259,"meta":188,"style":188},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import pandas as pd\n\nVS_RANGES = {\n    \"SYSBP\":  {\"low\": 60, \"high\": 250, \"unit\": \"mmHg\"},\n    \"DIABP\":  {\"low\": 30, \"high\": 150, \"unit\": \"mmHg\"},\n    \"HR\":     {\"low\": 30, \"high\": 220, \"unit\": \"beats\u002Fmin\"},\n    \"TEMP\":   {\"low\": 34.0, \"high\": 42.0, \"unit\": \"C\"},\n    \"WEIGHT\": {\"low\": 20, \"high\": 300, \"unit\": \"kg\"},\n    \"HEIGHT\": {\"low\": 50, \"high\": 250, \"unit\": \"cm\"},\n    \"RESP\":   {\"low\": 6, \"high\": 60, \"unit\": \"breaths\u002Fmin\"},\n}\n\nLB_RANGES = {\n    \"ALT\":   {\"low\": 0, \"high\": 500, \"unit\": \"U\u002FL\"},\n    \"AST\":   {\"low\": 0, \"high\": 500, \"unit\": \"U\u002FL\"},\n    \"BILI\":  {\"low\": 0, \"high\": 30, \"unit\": \"mg\u002FdL\"},\n    \"CREAT\": {\"low\": 0, \"high\": 20, \"unit\": \"mg\u002FdL\"},\n    \"HGB\":   {\"low\": 3, \"high\": 25, \"unit\": \"g\u002FdL\"},\n    \"WBC\":   {\"low\": 0, \"high\": 100, \"unit\": \"10^9\u002FL\"},\n    \"PLAT\":  {\"low\": 0, \"high\": 1500, \"unit\": \"10^9\u002FL\"},\n    \"GLUC\":  {\"low\": 10, \"high\": 1000, \"unit\": \"mg\u002FdL\"},\n}\n\n# Pediatric multipliers (apply to VS_RANGES when age \u003C 18)\nPEDIATRIC_MULT = {\"SYSBP_low\": 0.7, \"SYSBP_high\": 0.8, \"HR_high\": 1.3}\n\n# Hy's Law: ALT or AST >3×ULN AND BILI >2×ULN (ULN: ALT=40, AST=37, BILI=1.2)\nHYS_LAW = {\"ALT_ULN\": 40, \"AST_ULN\": 37, \"BILI_ULN\": 1.2, \"ALT_MULT\": 3, \"BILI_MULT\": 2}\n\ndef check_ranges(df: pd.DataFrame, testcd_col: str, value_col: str,\n                 ranges: dict, domain: str) -> pd.DataFrame:\n    issues = []\n    for testcd, limits in ranges.items():\n        subset = df[df[testcd_col] == testcd]\n        oob = subset[(subset[value_col] \u003C limits[\"low\"]) | (subset[value_col] > limits[\"high\"])]\n        for _, row in oob.iterrows():\n            severity = \"HIGH\" if testcd in (\"SYSBP\", \"HR\", \"ALT\", \"AST\", \"BILI\") else \"MEDIUM\"\n            issues.append({\"USUBJID\": row.get(\"USUBJID\"), \"DOMAIN\": domain,\n                           \"TESTCD\": testcd, \"VALUE\": row[value_col],\n                           \"EXPECTED\": f\"{limits['low']}–{limits['high']} {limits['unit']}\",\n                           \"SEVERITY\": severity})\n    return pd.DataFrame(issues)\n","python",[261],{"type":73,"tag":186,"props":262,"children":263},{"__ignoreMap":188},[264,275,285,294,302,311,320,329,338,347,356,365,373,382,391,400,409,418,427,436,445,454,462,470,479,488,496,505,514,522,531,540,549,558,567,576,585,594,603,612,621,630],{"type":73,"tag":265,"props":266,"children":269},"span",{"class":267,"line":268},"line",1,[270],{"type":73,"tag":265,"props":271,"children":272},{},[273],{"type":79,"value":274},"import pandas as pd\n",{"type":73,"tag":265,"props":276,"children":278},{"class":267,"line":277},2,[279],{"type":73,"tag":265,"props":280,"children":282},{"emptyLinePlaceholder":281},true,[283],{"type":79,"value":284},"\n",{"type":73,"tag":265,"props":286,"children":288},{"class":267,"line":287},3,[289],{"type":73,"tag":265,"props":290,"children":291},{},[292],{"type":79,"value":293},"VS_RANGES = {\n",{"type":73,"tag":265,"props":295,"children":296},{"class":267,"line":26},[297],{"type":73,"tag":265,"props":298,"children":299},{},[300],{"type":79,"value":301},"    \"SYSBP\":  {\"low\": 60, \"high\": 250, \"unit\": \"mmHg\"},\n",{"type":73,"tag":265,"props":303,"children":305},{"class":267,"line":304},5,[306],{"type":73,"tag":265,"props":307,"children":308},{},[309],{"type":79,"value":310},"    \"DIABP\":  {\"low\": 30, \"high\": 150, \"unit\": \"mmHg\"},\n",{"type":73,"tag":265,"props":312,"children":314},{"class":267,"line":313},6,[315],{"type":73,"tag":265,"props":316,"children":317},{},[318],{"type":79,"value":319},"    \"HR\":     {\"low\": 30, \"high\": 220, \"unit\": \"beats\u002Fmin\"},\n",{"type":73,"tag":265,"props":321,"children":323},{"class":267,"line":322},7,[324],{"type":73,"tag":265,"props":325,"children":326},{},[327],{"type":79,"value":328},"    \"TEMP\":   {\"low\": 34.0, \"high\": 42.0, \"unit\": \"C\"},\n",{"type":73,"tag":265,"props":330,"children":332},{"class":267,"line":331},8,[333],{"type":73,"tag":265,"props":334,"children":335},{},[336],{"type":79,"value":337},"    \"WEIGHT\": {\"low\": 20, \"high\": 300, \"unit\": \"kg\"},\n",{"type":73,"tag":265,"props":339,"children":341},{"class":267,"line":340},9,[342],{"type":73,"tag":265,"props":343,"children":344},{},[345],{"type":79,"value":346},"    \"HEIGHT\": {\"low\": 50, \"high\": 250, \"unit\": \"cm\"},\n",{"type":73,"tag":265,"props":348,"children":350},{"class":267,"line":349},10,[351],{"type":73,"tag":265,"props":352,"children":353},{},[354],{"type":79,"value":355},"    \"RESP\":   {\"low\": 6, \"high\": 60, \"unit\": \"breaths\u002Fmin\"},\n",{"type":73,"tag":265,"props":357,"children":359},{"class":267,"line":358},11,[360],{"type":73,"tag":265,"props":361,"children":362},{},[363],{"type":79,"value":364},"}\n",{"type":73,"tag":265,"props":366,"children":368},{"class":267,"line":367},12,[369],{"type":73,"tag":265,"props":370,"children":371},{"emptyLinePlaceholder":281},[372],{"type":79,"value":284},{"type":73,"tag":265,"props":374,"children":376},{"class":267,"line":375},13,[377],{"type":73,"tag":265,"props":378,"children":379},{},[380],{"type":79,"value":381},"LB_RANGES = {\n",{"type":73,"tag":265,"props":383,"children":385},{"class":267,"line":384},14,[386],{"type":73,"tag":265,"props":387,"children":388},{},[389],{"type":79,"value":390},"    \"ALT\":   {\"low\": 0, \"high\": 500, \"unit\": \"U\u002FL\"},\n",{"type":73,"tag":265,"props":392,"children":394},{"class":267,"line":393},15,[395],{"type":73,"tag":265,"props":396,"children":397},{},[398],{"type":79,"value":399},"    \"AST\":   {\"low\": 0, \"high\": 500, \"unit\": \"U\u002FL\"},\n",{"type":73,"tag":265,"props":401,"children":403},{"class":267,"line":402},16,[404],{"type":73,"tag":265,"props":405,"children":406},{},[407],{"type":79,"value":408},"    \"BILI\":  {\"low\": 0, \"high\": 30, \"unit\": \"mg\u002FdL\"},\n",{"type":73,"tag":265,"props":410,"children":412},{"class":267,"line":411},17,[413],{"type":73,"tag":265,"props":414,"children":415},{},[416],{"type":79,"value":417},"    \"CREAT\": {\"low\": 0, \"high\": 20, \"unit\": \"mg\u002FdL\"},\n",{"type":73,"tag":265,"props":419,"children":421},{"class":267,"line":420},18,[422],{"type":73,"tag":265,"props":423,"children":424},{},[425],{"type":79,"value":426},"    \"HGB\":   {\"low\": 3, \"high\": 25, \"unit\": \"g\u002FdL\"},\n",{"type":73,"tag":265,"props":428,"children":430},{"class":267,"line":429},19,[431],{"type":73,"tag":265,"props":432,"children":433},{},[434],{"type":79,"value":435},"    \"WBC\":   {\"low\": 0, \"high\": 100, \"unit\": \"10^9\u002FL\"},\n",{"type":73,"tag":265,"props":437,"children":439},{"class":267,"line":438},20,[440],{"type":73,"tag":265,"props":441,"children":442},{},[443],{"type":79,"value":444},"    \"PLAT\":  {\"low\": 0, \"high\": 1500, \"unit\": \"10^9\u002FL\"},\n",{"type":73,"tag":265,"props":446,"children":448},{"class":267,"line":447},21,[449],{"type":73,"tag":265,"props":450,"children":451},{},[452],{"type":79,"value":453},"    \"GLUC\":  {\"low\": 10, \"high\": 1000, \"unit\": \"mg\u002FdL\"},\n",{"type":73,"tag":265,"props":455,"children":457},{"class":267,"line":456},22,[458],{"type":73,"tag":265,"props":459,"children":460},{},[461],{"type":79,"value":364},{"type":73,"tag":265,"props":463,"children":465},{"class":267,"line":464},23,[466],{"type":73,"tag":265,"props":467,"children":468},{"emptyLinePlaceholder":281},[469],{"type":79,"value":284},{"type":73,"tag":265,"props":471,"children":473},{"class":267,"line":472},24,[474],{"type":73,"tag":265,"props":475,"children":476},{},[477],{"type":79,"value":478},"# Pediatric multipliers (apply to VS_RANGES when age \u003C 18)\n",{"type":73,"tag":265,"props":480,"children":482},{"class":267,"line":481},25,[483],{"type":73,"tag":265,"props":484,"children":485},{},[486],{"type":79,"value":487},"PEDIATRIC_MULT = {\"SYSBP_low\": 0.7, \"SYSBP_high\": 0.8, \"HR_high\": 1.3}\n",{"type":73,"tag":265,"props":489,"children":491},{"class":267,"line":490},26,[492],{"type":73,"tag":265,"props":493,"children":494},{"emptyLinePlaceholder":281},[495],{"type":79,"value":284},{"type":73,"tag":265,"props":497,"children":499},{"class":267,"line":498},27,[500],{"type":73,"tag":265,"props":501,"children":502},{},[503],{"type":79,"value":504},"# Hy's Law: ALT or AST >3×ULN AND BILI >2×ULN (ULN: ALT=40, AST=37, BILI=1.2)\n",{"type":73,"tag":265,"props":506,"children":508},{"class":267,"line":507},28,[509],{"type":73,"tag":265,"props":510,"children":511},{},[512],{"type":79,"value":513},"HYS_LAW = {\"ALT_ULN\": 40, \"AST_ULN\": 37, \"BILI_ULN\": 1.2, \"ALT_MULT\": 3, \"BILI_MULT\": 2}\n",{"type":73,"tag":265,"props":515,"children":517},{"class":267,"line":516},29,[518],{"type":73,"tag":265,"props":519,"children":520},{"emptyLinePlaceholder":281},[521],{"type":79,"value":284},{"type":73,"tag":265,"props":523,"children":525},{"class":267,"line":524},30,[526],{"type":73,"tag":265,"props":527,"children":528},{},[529],{"type":79,"value":530},"def check_ranges(df: pd.DataFrame, testcd_col: str, value_col: str,\n",{"type":73,"tag":265,"props":532,"children":534},{"class":267,"line":533},31,[535],{"type":73,"tag":265,"props":536,"children":537},{},[538],{"type":79,"value":539},"                 ranges: dict, domain: str) -> pd.DataFrame:\n",{"type":73,"tag":265,"props":541,"children":543},{"class":267,"line":542},32,[544],{"type":73,"tag":265,"props":545,"children":546},{},[547],{"type":79,"value":548},"    issues = []\n",{"type":73,"tag":265,"props":550,"children":552},{"class":267,"line":551},33,[553],{"type":73,"tag":265,"props":554,"children":555},{},[556],{"type":79,"value":557},"    for testcd, limits in ranges.items():\n",{"type":73,"tag":265,"props":559,"children":561},{"class":267,"line":560},34,[562],{"type":73,"tag":265,"props":563,"children":564},{},[565],{"type":79,"value":566},"        subset = df[df[testcd_col] == testcd]\n",{"type":73,"tag":265,"props":568,"children":570},{"class":267,"line":569},35,[571],{"type":73,"tag":265,"props":572,"children":573},{},[574],{"type":79,"value":575},"        oob = subset[(subset[value_col] \u003C limits[\"low\"]) | (subset[value_col] > limits[\"high\"])]\n",{"type":73,"tag":265,"props":577,"children":579},{"class":267,"line":578},36,[580],{"type":73,"tag":265,"props":581,"children":582},{},[583],{"type":79,"value":584},"        for _, row in oob.iterrows():\n",{"type":73,"tag":265,"props":586,"children":588},{"class":267,"line":587},37,[589],{"type":73,"tag":265,"props":590,"children":591},{},[592],{"type":79,"value":593},"            severity = \"HIGH\" if testcd in (\"SYSBP\", \"HR\", \"ALT\", \"AST\", \"BILI\") else \"MEDIUM\"\n",{"type":73,"tag":265,"props":595,"children":597},{"class":267,"line":596},38,[598],{"type":73,"tag":265,"props":599,"children":600},{},[601],{"type":79,"value":602},"            issues.append({\"USUBJID\": row.get(\"USUBJID\"), \"DOMAIN\": domain,\n",{"type":73,"tag":265,"props":604,"children":606},{"class":267,"line":605},39,[607],{"type":73,"tag":265,"props":608,"children":609},{},[610],{"type":79,"value":611},"                           \"TESTCD\": testcd, \"VALUE\": row[value_col],\n",{"type":73,"tag":265,"props":613,"children":615},{"class":267,"line":614},40,[616],{"type":73,"tag":265,"props":617,"children":618},{},[619],{"type":79,"value":620},"                           \"EXPECTED\": f\"{limits['low']}–{limits['high']} {limits['unit']}\",\n",{"type":73,"tag":265,"props":622,"children":624},{"class":267,"line":623},41,[625],{"type":73,"tag":265,"props":626,"children":627},{},[628],{"type":79,"value":629},"                           \"SEVERITY\": severity})\n",{"type":73,"tag":265,"props":631,"children":633},{"class":267,"line":632},42,[634],{"type":73,"tag":265,"props":635,"children":636},{},[637],{"type":79,"value":638},"    return pd.DataFrame(issues)\n",{"type":73,"tag":127,"props":640,"children":641},{},[],{"type":73,"tag":82,"props":643,"children":645},{"id":644},"_4-cross-form-consistency-checks-8-checks",[646],{"type":79,"value":647},"4. Cross-Form Consistency Checks (8 checks)",{"type":73,"tag":179,"props":649,"children":651},{"className":257,"code":650,"language":259,"meta":188,"style":188},"def cross_form_checks(datasets: dict[str, pd.DataFrame]) -> pd.DataFrame:\n    ae, ds, dm = datasets.get(\"AE\", pd.DataFrame()), datasets.get(\"DS\", pd.DataFrame()), datasets.get(\"DM\", pd.DataFrame())\n    sv, cm, ex = datasets.get(\"SV\", pd.DataFrame()), datasets.get(\"CM\", pd.DataFrame()), datasets.get(\"EX\", pd.DataFrame())\n    issues = []\n    to_dt = lambda s: pd.to_datetime(s, errors=\"coerce\")\n\n    # 1. AE start \u003C informed consent → CRITICAL\n    if not ae.empty and \"RFICDTC\" in dm.columns:\n        ic = dm.set_index(\"USUBJID\")[\"RFICDTC\"].apply(to_dt)\n        for _, r in ae.assign(AESTDTC=to_dt(ae[\"AESTDTC\"])).iterrows():\n            ic_dt = ic.get(r[\"USUBJID\"])\n            if pd.notna(ic_dt) and pd.notna(r[\"AESTDTC\"]) and r[\"AESTDTC\"] \u003C ic_dt:\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"AE_BEFORE_CONSENT\", \"SEVERITY\": \"CRITICAL\"})\n\n    # 2. Fatal AE without DS death record → CRITICAL\n    if not ae.empty and not ds.empty and \"DSDECOD\" in ds.columns:\n        death_subj = set(ds[ds[\"DSDECOD\"] == \"DEATH\"][\"USUBJID\"])\n        for _, r in ae[ae[\"AEOUT\"] == \"FATAL\"].iterrows():\n            if r[\"USUBJID\"] not in death_subj:\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"FATAL_AE_NO_DEATH\", \"SEVERITY\": \"CRITICAL\"})\n\n    # 3. Disposition date > last visit + 7 days → HIGH\n    if not ds.empty and not sv.empty:\n        last_sv = sv.groupby(\"USUBJID\")[\"SVSTDTC\"].max().apply(to_dt)\n        for _, r in ds.assign(DSSTDTC=to_dt(ds[\"DSSTDTC\"])).iterrows():\n            lv = last_sv.get(r[\"USUBJID\"])\n            if pd.notna(lv) and pd.notna(r[\"DSSTDTC\"]) and r[\"DSSTDTC\"] > lv + pd.Timedelta(days=7):\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"DS_AFTER_LAST_VISIT\", \"SEVERITY\": \"HIGH\"})\n\n    # 4. Lab collection on non-visit day → MEDIUM\n    if not datasets.get(\"LB\", pd.DataFrame()).empty and not sv.empty:\n        sv_set = set(zip(sv[\"USUBJID\"], to_dt(sv[\"SVSTDTC\"]).dt.date))\n        lb = datasets[\"LB\"].assign(LBDTC=to_dt(datasets[\"LB\"].get(\"LBDTC\")))\n        for _, r in lb.dropna(subset=[\"LBDTC\"]).iterrows():\n            if (r[\"USUBJID\"], r[\"LBDTC\"].date()) not in sv_set:\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"LB_NO_VISIT\", \"SEVERITY\": \"MEDIUM\"})\n\n    # 5. Conmed start after study completion → MEDIUM\n    if not cm.empty and \"RFPENDTC\" in dm.columns:\n        end = dm.set_index(\"USUBJID\")[\"RFPENDTC\"].apply(to_dt)\n        for _, r in cm.assign(CMSTDTC=to_dt(cm.get(\"CMSTDTC\"))).dropna(subset=[\"CMSTDTC\"]).iterrows():\n            e = end.get(r[\"USUBJID\"])\n            if pd.notna(e) and r[\"CMSTDTC\"] > e:\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"CM_AFTER_COMPLETION\", \"SEVERITY\": \"MEDIUM\"})\n\n    # 6. Randomization without signed consent → CRITICAL\n    if not dm.empty and \"RFICDTC\" in dm.columns and \"ARMCD\" in dm.columns:\n        for _, r in dm.iterrows():\n            if pd.notna(r.get(\"ARMCD\")) and r[\"ARMCD\"] != \"SCRNFAIL\" and pd.isna(r.get(\"RFICDTC\")):\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"RAND_NO_CONSENT\", \"SEVERITY\": \"CRITICAL\"})\n\n    # 7. Unscheduled visit without comment → MEDIUM\n    if not sv.empty and \"SVUPDES\" in sv.columns:\n        unsched = sv[sv[\"VISITNUM\"].astype(str).str.contains(r\"\\.\", na=False)]\n        no_comment = unsched[unsched[\"SVUPDES\"].isna() | (unsched[\"SVUPDES\"].str.strip() == \"\")]\n        for _, r in no_comment.iterrows():\n            issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"UNSCHED_NO_COMMENT\", \"SEVERITY\": \"MEDIUM\"})\n\n    # 8. Dose modification without corresponding AE → HIGH\n    if not ex.empty and not ae.empty and \"EXADJ\" in ex.columns:\n        ae_subj = set(zip(ae[\"USUBJID\"], to_dt(ae.get(\"AESTDTC\")).dt.date))\n        dose_mod = ex[ex[\"EXADJ\"].notna() & (ex[\"EXADJ\"] != \"\")].assign(EXSTDTC=to_dt(ex[\"EXSTDTC\"]))\n        for _, r in dose_mod.dropna(subset=[\"EXSTDTC\"]).iterrows():\n            if (r[\"USUBJID\"], r[\"EXSTDTC\"].date()) not in ae_subj:\n                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"DOSE_MOD_NO_AE\", \"SEVERITY\": \"HIGH\"})\n\n    return pd.DataFrame(issues)\n",[652],{"type":73,"tag":186,"props":653,"children":654},{"__ignoreMap":188},[655,663,671,679,686,694,701,709,717,725,733,741,749,757,764,772,780,788,796,804,812,819,827,835,843,851,859,867,875,882,890,898,906,914,922,930,938,945,953,961,969,977,985,994,1003,1011,1020,1029,1038,1047,1056,1064,1073,1082,1091,1100,1109,1118,1126,1135,1144,1153,1162,1171,1180,1189,1197],{"type":73,"tag":265,"props":656,"children":657},{"class":267,"line":268},[658],{"type":73,"tag":265,"props":659,"children":660},{},[661],{"type":79,"value":662},"def cross_form_checks(datasets: dict[str, pd.DataFrame]) -> pd.DataFrame:\n",{"type":73,"tag":265,"props":664,"children":665},{"class":267,"line":277},[666],{"type":73,"tag":265,"props":667,"children":668},{},[669],{"type":79,"value":670},"    ae, ds, dm = datasets.get(\"AE\", pd.DataFrame()), datasets.get(\"DS\", pd.DataFrame()), datasets.get(\"DM\", pd.DataFrame())\n",{"type":73,"tag":265,"props":672,"children":673},{"class":267,"line":287},[674],{"type":73,"tag":265,"props":675,"children":676},{},[677],{"type":79,"value":678},"    sv, cm, ex = datasets.get(\"SV\", pd.DataFrame()), datasets.get(\"CM\", pd.DataFrame()), datasets.get(\"EX\", pd.DataFrame())\n",{"type":73,"tag":265,"props":680,"children":681},{"class":267,"line":26},[682],{"type":73,"tag":265,"props":683,"children":684},{},[685],{"type":79,"value":548},{"type":73,"tag":265,"props":687,"children":688},{"class":267,"line":304},[689],{"type":73,"tag":265,"props":690,"children":691},{},[692],{"type":79,"value":693},"    to_dt = lambda s: pd.to_datetime(s, errors=\"coerce\")\n",{"type":73,"tag":265,"props":695,"children":696},{"class":267,"line":313},[697],{"type":73,"tag":265,"props":698,"children":699},{"emptyLinePlaceholder":281},[700],{"type":79,"value":284},{"type":73,"tag":265,"props":702,"children":703},{"class":267,"line":322},[704],{"type":73,"tag":265,"props":705,"children":706},{},[707],{"type":79,"value":708},"    # 1. AE start \u003C informed consent → CRITICAL\n",{"type":73,"tag":265,"props":710,"children":711},{"class":267,"line":331},[712],{"type":73,"tag":265,"props":713,"children":714},{},[715],{"type":79,"value":716},"    if not ae.empty and \"RFICDTC\" in dm.columns:\n",{"type":73,"tag":265,"props":718,"children":719},{"class":267,"line":340},[720],{"type":73,"tag":265,"props":721,"children":722},{},[723],{"type":79,"value":724},"        ic = dm.set_index(\"USUBJID\")[\"RFICDTC\"].apply(to_dt)\n",{"type":73,"tag":265,"props":726,"children":727},{"class":267,"line":349},[728],{"type":73,"tag":265,"props":729,"children":730},{},[731],{"type":79,"value":732},"        for _, r in ae.assign(AESTDTC=to_dt(ae[\"AESTDTC\"])).iterrows():\n",{"type":73,"tag":265,"props":734,"children":735},{"class":267,"line":358},[736],{"type":73,"tag":265,"props":737,"children":738},{},[739],{"type":79,"value":740},"            ic_dt = ic.get(r[\"USUBJID\"])\n",{"type":73,"tag":265,"props":742,"children":743},{"class":267,"line":367},[744],{"type":73,"tag":265,"props":745,"children":746},{},[747],{"type":79,"value":748},"            if pd.notna(ic_dt) and pd.notna(r[\"AESTDTC\"]) and r[\"AESTDTC\"] \u003C ic_dt:\n",{"type":73,"tag":265,"props":750,"children":751},{"class":267,"line":375},[752],{"type":73,"tag":265,"props":753,"children":754},{},[755],{"type":79,"value":756},"                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"AE_BEFORE_CONSENT\", \"SEVERITY\": \"CRITICAL\"})\n",{"type":73,"tag":265,"props":758,"children":759},{"class":267,"line":384},[760],{"type":73,"tag":265,"props":761,"children":762},{"emptyLinePlaceholder":281},[763],{"type":79,"value":284},{"type":73,"tag":265,"props":765,"children":766},{"class":267,"line":393},[767],{"type":73,"tag":265,"props":768,"children":769},{},[770],{"type":79,"value":771},"    # 2. Fatal AE without DS death record → CRITICAL\n",{"type":73,"tag":265,"props":773,"children":774},{"class":267,"line":402},[775],{"type":73,"tag":265,"props":776,"children":777},{},[778],{"type":79,"value":779},"    if not ae.empty and not ds.empty and \"DSDECOD\" in ds.columns:\n",{"type":73,"tag":265,"props":781,"children":782},{"class":267,"line":411},[783],{"type":73,"tag":265,"props":784,"children":785},{},[786],{"type":79,"value":787},"        death_subj = set(ds[ds[\"DSDECOD\"] == \"DEATH\"][\"USUBJID\"])\n",{"type":73,"tag":265,"props":789,"children":790},{"class":267,"line":420},[791],{"type":73,"tag":265,"props":792,"children":793},{},[794],{"type":79,"value":795},"        for _, r in ae[ae[\"AEOUT\"] == \"FATAL\"].iterrows():\n",{"type":73,"tag":265,"props":797,"children":798},{"class":267,"line":429},[799],{"type":73,"tag":265,"props":800,"children":801},{},[802],{"type":79,"value":803},"            if r[\"USUBJID\"] not in death_subj:\n",{"type":73,"tag":265,"props":805,"children":806},{"class":267,"line":438},[807],{"type":73,"tag":265,"props":808,"children":809},{},[810],{"type":79,"value":811},"                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"FATAL_AE_NO_DEATH\", \"SEVERITY\": \"CRITICAL\"})\n",{"type":73,"tag":265,"props":813,"children":814},{"class":267,"line":447},[815],{"type":73,"tag":265,"props":816,"children":817},{"emptyLinePlaceholder":281},[818],{"type":79,"value":284},{"type":73,"tag":265,"props":820,"children":821},{"class":267,"line":456},[822],{"type":73,"tag":265,"props":823,"children":824},{},[825],{"type":79,"value":826},"    # 3. Disposition date > last visit + 7 days → HIGH\n",{"type":73,"tag":265,"props":828,"children":829},{"class":267,"line":464},[830],{"type":73,"tag":265,"props":831,"children":832},{},[833],{"type":79,"value":834},"    if not ds.empty and not sv.empty:\n",{"type":73,"tag":265,"props":836,"children":837},{"class":267,"line":472},[838],{"type":73,"tag":265,"props":839,"children":840},{},[841],{"type":79,"value":842},"        last_sv = sv.groupby(\"USUBJID\")[\"SVSTDTC\"].max().apply(to_dt)\n",{"type":73,"tag":265,"props":844,"children":845},{"class":267,"line":481},[846],{"type":73,"tag":265,"props":847,"children":848},{},[849],{"type":79,"value":850},"        for _, r in ds.assign(DSSTDTC=to_dt(ds[\"DSSTDTC\"])).iterrows():\n",{"type":73,"tag":265,"props":852,"children":853},{"class":267,"line":490},[854],{"type":73,"tag":265,"props":855,"children":856},{},[857],{"type":79,"value":858},"            lv = last_sv.get(r[\"USUBJID\"])\n",{"type":73,"tag":265,"props":860,"children":861},{"class":267,"line":498},[862],{"type":73,"tag":265,"props":863,"children":864},{},[865],{"type":79,"value":866},"            if pd.notna(lv) and pd.notna(r[\"DSSTDTC\"]) and r[\"DSSTDTC\"] > lv + pd.Timedelta(days=7):\n",{"type":73,"tag":265,"props":868,"children":869},{"class":267,"line":507},[870],{"type":73,"tag":265,"props":871,"children":872},{},[873],{"type":79,"value":874},"                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"DS_AFTER_LAST_VISIT\", \"SEVERITY\": \"HIGH\"})\n",{"type":73,"tag":265,"props":876,"children":877},{"class":267,"line":516},[878],{"type":73,"tag":265,"props":879,"children":880},{"emptyLinePlaceholder":281},[881],{"type":79,"value":284},{"type":73,"tag":265,"props":883,"children":884},{"class":267,"line":524},[885],{"type":73,"tag":265,"props":886,"children":887},{},[888],{"type":79,"value":889},"    # 4. Lab collection on non-visit day → MEDIUM\n",{"type":73,"tag":265,"props":891,"children":892},{"class":267,"line":533},[893],{"type":73,"tag":265,"props":894,"children":895},{},[896],{"type":79,"value":897},"    if not datasets.get(\"LB\", pd.DataFrame()).empty and not sv.empty:\n",{"type":73,"tag":265,"props":899,"children":900},{"class":267,"line":542},[901],{"type":73,"tag":265,"props":902,"children":903},{},[904],{"type":79,"value":905},"        sv_set = set(zip(sv[\"USUBJID\"], to_dt(sv[\"SVSTDTC\"]).dt.date))\n",{"type":73,"tag":265,"props":907,"children":908},{"class":267,"line":551},[909],{"type":73,"tag":265,"props":910,"children":911},{},[912],{"type":79,"value":913},"        lb = datasets[\"LB\"].assign(LBDTC=to_dt(datasets[\"LB\"].get(\"LBDTC\")))\n",{"type":73,"tag":265,"props":915,"children":916},{"class":267,"line":560},[917],{"type":73,"tag":265,"props":918,"children":919},{},[920],{"type":79,"value":921},"        for _, r in lb.dropna(subset=[\"LBDTC\"]).iterrows():\n",{"type":73,"tag":265,"props":923,"children":924},{"class":267,"line":569},[925],{"type":73,"tag":265,"props":926,"children":927},{},[928],{"type":79,"value":929},"            if (r[\"USUBJID\"], r[\"LBDTC\"].date()) not in sv_set:\n",{"type":73,"tag":265,"props":931,"children":932},{"class":267,"line":578},[933],{"type":73,"tag":265,"props":934,"children":935},{},[936],{"type":79,"value":937},"                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"LB_NO_VISIT\", \"SEVERITY\": \"MEDIUM\"})\n",{"type":73,"tag":265,"props":939,"children":940},{"class":267,"line":587},[941],{"type":73,"tag":265,"props":942,"children":943},{"emptyLinePlaceholder":281},[944],{"type":79,"value":284},{"type":73,"tag":265,"props":946,"children":947},{"class":267,"line":596},[948],{"type":73,"tag":265,"props":949,"children":950},{},[951],{"type":79,"value":952},"    # 5. Conmed start after study completion → MEDIUM\n",{"type":73,"tag":265,"props":954,"children":955},{"class":267,"line":605},[956],{"type":73,"tag":265,"props":957,"children":958},{},[959],{"type":79,"value":960},"    if not cm.empty and \"RFPENDTC\" in dm.columns:\n",{"type":73,"tag":265,"props":962,"children":963},{"class":267,"line":614},[964],{"type":73,"tag":265,"props":965,"children":966},{},[967],{"type":79,"value":968},"        end = dm.set_index(\"USUBJID\")[\"RFPENDTC\"].apply(to_dt)\n",{"type":73,"tag":265,"props":970,"children":971},{"class":267,"line":623},[972],{"type":73,"tag":265,"props":973,"children":974},{},[975],{"type":79,"value":976},"        for _, r in cm.assign(CMSTDTC=to_dt(cm.get(\"CMSTDTC\"))).dropna(subset=[\"CMSTDTC\"]).iterrows():\n",{"type":73,"tag":265,"props":978,"children":979},{"class":267,"line":632},[980],{"type":73,"tag":265,"props":981,"children":982},{},[983],{"type":79,"value":984},"            e = end.get(r[\"USUBJID\"])\n",{"type":73,"tag":265,"props":986,"children":988},{"class":267,"line":987},43,[989],{"type":73,"tag":265,"props":990,"children":991},{},[992],{"type":79,"value":993},"            if pd.notna(e) and r[\"CMSTDTC\"] > e:\n",{"type":73,"tag":265,"props":995,"children":997},{"class":267,"line":996},44,[998],{"type":73,"tag":265,"props":999,"children":1000},{},[1001],{"type":79,"value":1002},"                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"CM_AFTER_COMPLETION\", \"SEVERITY\": \"MEDIUM\"})\n",{"type":73,"tag":265,"props":1004,"children":1006},{"class":267,"line":1005},45,[1007],{"type":73,"tag":265,"props":1008,"children":1009},{"emptyLinePlaceholder":281},[1010],{"type":79,"value":284},{"type":73,"tag":265,"props":1012,"children":1014},{"class":267,"line":1013},46,[1015],{"type":73,"tag":265,"props":1016,"children":1017},{},[1018],{"type":79,"value":1019},"    # 6. Randomization without signed consent → CRITICAL\n",{"type":73,"tag":265,"props":1021,"children":1023},{"class":267,"line":1022},47,[1024],{"type":73,"tag":265,"props":1025,"children":1026},{},[1027],{"type":79,"value":1028},"    if not dm.empty and \"RFICDTC\" in dm.columns and \"ARMCD\" in dm.columns:\n",{"type":73,"tag":265,"props":1030,"children":1032},{"class":267,"line":1031},48,[1033],{"type":73,"tag":265,"props":1034,"children":1035},{},[1036],{"type":79,"value":1037},"        for _, r in dm.iterrows():\n",{"type":73,"tag":265,"props":1039,"children":1041},{"class":267,"line":1040},49,[1042],{"type":73,"tag":265,"props":1043,"children":1044},{},[1045],{"type":79,"value":1046},"            if pd.notna(r.get(\"ARMCD\")) and r[\"ARMCD\"] != \"SCRNFAIL\" and pd.isna(r.get(\"RFICDTC\")):\n",{"type":73,"tag":265,"props":1048,"children":1050},{"class":267,"line":1049},50,[1051],{"type":73,"tag":265,"props":1052,"children":1053},{},[1054],{"type":79,"value":1055},"                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"RAND_NO_CONSENT\", \"SEVERITY\": \"CRITICAL\"})\n",{"type":73,"tag":265,"props":1057,"children":1059},{"class":267,"line":1058},51,[1060],{"type":73,"tag":265,"props":1061,"children":1062},{"emptyLinePlaceholder":281},[1063],{"type":79,"value":284},{"type":73,"tag":265,"props":1065,"children":1067},{"class":267,"line":1066},52,[1068],{"type":73,"tag":265,"props":1069,"children":1070},{},[1071],{"type":79,"value":1072},"    # 7. Unscheduled visit without comment → MEDIUM\n",{"type":73,"tag":265,"props":1074,"children":1076},{"class":267,"line":1075},53,[1077],{"type":73,"tag":265,"props":1078,"children":1079},{},[1080],{"type":79,"value":1081},"    if not sv.empty and \"SVUPDES\" in sv.columns:\n",{"type":73,"tag":265,"props":1083,"children":1085},{"class":267,"line":1084},54,[1086],{"type":73,"tag":265,"props":1087,"children":1088},{},[1089],{"type":79,"value":1090},"        unsched = sv[sv[\"VISITNUM\"].astype(str).str.contains(r\"\\.\", na=False)]\n",{"type":73,"tag":265,"props":1092,"children":1094},{"class":267,"line":1093},55,[1095],{"type":73,"tag":265,"props":1096,"children":1097},{},[1098],{"type":79,"value":1099},"        no_comment = unsched[unsched[\"SVUPDES\"].isna() | (unsched[\"SVUPDES\"].str.strip() == \"\")]\n",{"type":73,"tag":265,"props":1101,"children":1103},{"class":267,"line":1102},56,[1104],{"type":73,"tag":265,"props":1105,"children":1106},{},[1107],{"type":79,"value":1108},"        for _, r in no_comment.iterrows():\n",{"type":73,"tag":265,"props":1110,"children":1112},{"class":267,"line":1111},57,[1113],{"type":73,"tag":265,"props":1114,"children":1115},{},[1116],{"type":79,"value":1117},"            issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"UNSCHED_NO_COMMENT\", \"SEVERITY\": \"MEDIUM\"})\n",{"type":73,"tag":265,"props":1119,"children":1121},{"class":267,"line":1120},58,[1122],{"type":73,"tag":265,"props":1123,"children":1124},{"emptyLinePlaceholder":281},[1125],{"type":79,"value":284},{"type":73,"tag":265,"props":1127,"children":1129},{"class":267,"line":1128},59,[1130],{"type":73,"tag":265,"props":1131,"children":1132},{},[1133],{"type":79,"value":1134},"    # 8. Dose modification without corresponding AE → HIGH\n",{"type":73,"tag":265,"props":1136,"children":1138},{"class":267,"line":1137},60,[1139],{"type":73,"tag":265,"props":1140,"children":1141},{},[1142],{"type":79,"value":1143},"    if not ex.empty and not ae.empty and \"EXADJ\" in ex.columns:\n",{"type":73,"tag":265,"props":1145,"children":1147},{"class":267,"line":1146},61,[1148],{"type":73,"tag":265,"props":1149,"children":1150},{},[1151],{"type":79,"value":1152},"        ae_subj = set(zip(ae[\"USUBJID\"], to_dt(ae.get(\"AESTDTC\")).dt.date))\n",{"type":73,"tag":265,"props":1154,"children":1156},{"class":267,"line":1155},62,[1157],{"type":73,"tag":265,"props":1158,"children":1159},{},[1160],{"type":79,"value":1161},"        dose_mod = ex[ex[\"EXADJ\"].notna() & (ex[\"EXADJ\"] != \"\")].assign(EXSTDTC=to_dt(ex[\"EXSTDTC\"]))\n",{"type":73,"tag":265,"props":1163,"children":1165},{"class":267,"line":1164},63,[1166],{"type":73,"tag":265,"props":1167,"children":1168},{},[1169],{"type":79,"value":1170},"        for _, r in dose_mod.dropna(subset=[\"EXSTDTC\"]).iterrows():\n",{"type":73,"tag":265,"props":1172,"children":1174},{"class":267,"line":1173},64,[1175],{"type":73,"tag":265,"props":1176,"children":1177},{},[1178],{"type":79,"value":1179},"            if (r[\"USUBJID\"], r[\"EXSTDTC\"].date()) not in ae_subj:\n",{"type":73,"tag":265,"props":1181,"children":1183},{"class":267,"line":1182},65,[1184],{"type":73,"tag":265,"props":1185,"children":1186},{},[1187],{"type":79,"value":1188},"                issues.append({\"USUBJID\": r[\"USUBJID\"], \"CHECK\": \"DOSE_MOD_NO_AE\", \"SEVERITY\": \"HIGH\"})\n",{"type":73,"tag":265,"props":1190,"children":1192},{"class":267,"line":1191},66,[1193],{"type":73,"tag":265,"props":1194,"children":1195},{"emptyLinePlaceholder":281},[1196],{"type":79,"value":284},{"type":73,"tag":265,"props":1198,"children":1200},{"class":267,"line":1199},67,[1201],{"type":73,"tag":265,"props":1202,"children":1203},{},[1204],{"type":79,"value":638},{"type":73,"tag":127,"props":1206,"children":1207},{},[],{"type":73,"tag":82,"props":1209,"children":1211},{"id":1210},"_5-partial-date-imputation",[1212],{"type":79,"value":1213},"5. Partial Date Imputation",{"type":73,"tag":179,"props":1215,"children":1217},{"className":257,"code":1216,"language":259,"meta":188,"style":188},"import calendar\n\ndef impute_partial_date(dtc: str, position: str = \"start\") -> tuple[str, str]:\n    \"\"\"Impute partial ISO 8601 dates. Returns (imputed_date, DTYPE).\n    position: 'start' imputes early, 'end' imputes late.\"\"\"\n    if pd.isna(dtc) or dtc.strip() == \"\":\n        return (None, None)\n    parts = dtc.split(\"-\")\n    if len(parts) == 3 and len(parts[2]) >= 2:\n        return (dtc, None)  # Complete date, no imputation\n    year = int(parts[0])\n    if len(parts) == 1:  # YYYY only\n        if position == \"start\":\n            return (f\"{year}-01-01\", \"FIRST\")\n        return (f\"{year}-12-31\", \"LAST\")\n    month = int(parts[1])\n    if len(parts) == 2:  # YYYY-MM only\n        if position == \"start\":\n            return (f\"{year}-{month:02d}-01\", \"FIRST\")\n        last_day = calendar.monthrange(year, month)[1]\n        return (f\"{year}-{month:02d}-{last_day}\", \"LAST\")\n    return (dtc, None)\n",[1218],{"type":73,"tag":186,"props":1219,"children":1220},{"__ignoreMap":188},[1221,1229,1236,1244,1252,1260,1268,1276,1284,1292,1300,1308,1316,1324,1332,1340,1348,1356,1363,1371,1379,1387],{"type":73,"tag":265,"props":1222,"children":1223},{"class":267,"line":268},[1224],{"type":73,"tag":265,"props":1225,"children":1226},{},[1227],{"type":79,"value":1228},"import calendar\n",{"type":73,"tag":265,"props":1230,"children":1231},{"class":267,"line":277},[1232],{"type":73,"tag":265,"props":1233,"children":1234},{"emptyLinePlaceholder":281},[1235],{"type":79,"value":284},{"type":73,"tag":265,"props":1237,"children":1238},{"class":267,"line":287},[1239],{"type":73,"tag":265,"props":1240,"children":1241},{},[1242],{"type":79,"value":1243},"def impute_partial_date(dtc: str, position: str = \"start\") -> tuple[str, str]:\n",{"type":73,"tag":265,"props":1245,"children":1246},{"class":267,"line":26},[1247],{"type":73,"tag":265,"props":1248,"children":1249},{},[1250],{"type":79,"value":1251},"    \"\"\"Impute partial ISO 8601 dates. Returns (imputed_date, DTYPE).\n",{"type":73,"tag":265,"props":1253,"children":1254},{"class":267,"line":304},[1255],{"type":73,"tag":265,"props":1256,"children":1257},{},[1258],{"type":79,"value":1259},"    position: 'start' imputes early, 'end' imputes late.\"\"\"\n",{"type":73,"tag":265,"props":1261,"children":1262},{"class":267,"line":313},[1263],{"type":73,"tag":265,"props":1264,"children":1265},{},[1266],{"type":79,"value":1267},"    if pd.isna(dtc) or dtc.strip() == \"\":\n",{"type":73,"tag":265,"props":1269,"children":1270},{"class":267,"line":322},[1271],{"type":73,"tag":265,"props":1272,"children":1273},{},[1274],{"type":79,"value":1275},"        return (None, None)\n",{"type":73,"tag":265,"props":1277,"children":1278},{"class":267,"line":331},[1279],{"type":73,"tag":265,"props":1280,"children":1281},{},[1282],{"type":79,"value":1283},"    parts = dtc.split(\"-\")\n",{"type":73,"tag":265,"props":1285,"children":1286},{"class":267,"line":340},[1287],{"type":73,"tag":265,"props":1288,"children":1289},{},[1290],{"type":79,"value":1291},"    if len(parts) == 3 and len(parts[2]) >= 2:\n",{"type":73,"tag":265,"props":1293,"children":1294},{"class":267,"line":349},[1295],{"type":73,"tag":265,"props":1296,"children":1297},{},[1298],{"type":79,"value":1299},"        return (dtc, None)  # Complete date, no imputation\n",{"type":73,"tag":265,"props":1301,"children":1302},{"class":267,"line":358},[1303],{"type":73,"tag":265,"props":1304,"children":1305},{},[1306],{"type":79,"value":1307},"    year = int(parts[0])\n",{"type":73,"tag":265,"props":1309,"children":1310},{"class":267,"line":367},[1311],{"type":73,"tag":265,"props":1312,"children":1313},{},[1314],{"type":79,"value":1315},"    if len(parts) == 1:  # YYYY only\n",{"type":73,"tag":265,"props":1317,"children":1318},{"class":267,"line":375},[1319],{"type":73,"tag":265,"props":1320,"children":1321},{},[1322],{"type":79,"value":1323},"        if position == \"start\":\n",{"type":73,"tag":265,"props":1325,"children":1326},{"class":267,"line":384},[1327],{"type":73,"tag":265,"props":1328,"children":1329},{},[1330],{"type":79,"value":1331},"            return (f\"{year}-01-01\", \"FIRST\")\n",{"type":73,"tag":265,"props":1333,"children":1334},{"class":267,"line":393},[1335],{"type":73,"tag":265,"props":1336,"children":1337},{},[1338],{"type":79,"value":1339},"        return (f\"{year}-12-31\", \"LAST\")\n",{"type":73,"tag":265,"props":1341,"children":1342},{"class":267,"line":402},[1343],{"type":73,"tag":265,"props":1344,"children":1345},{},[1346],{"type":79,"value":1347},"    month = int(parts[1])\n",{"type":73,"tag":265,"props":1349,"children":1350},{"class":267,"line":411},[1351],{"type":73,"tag":265,"props":1352,"children":1353},{},[1354],{"type":79,"value":1355},"    if len(parts) == 2:  # YYYY-MM only\n",{"type":73,"tag":265,"props":1357,"children":1358},{"class":267,"line":420},[1359],{"type":73,"tag":265,"props":1360,"children":1361},{},[1362],{"type":79,"value":1323},{"type":73,"tag":265,"props":1364,"children":1365},{"class":267,"line":429},[1366],{"type":73,"tag":265,"props":1367,"children":1368},{},[1369],{"type":79,"value":1370},"            return (f\"{year}-{month:02d}-01\", \"FIRST\")\n",{"type":73,"tag":265,"props":1372,"children":1373},{"class":267,"line":438},[1374],{"type":73,"tag":265,"props":1375,"children":1376},{},[1377],{"type":79,"value":1378},"        last_day = calendar.monthrange(year, month)[1]\n",{"type":73,"tag":265,"props":1380,"children":1381},{"class":267,"line":447},[1382],{"type":73,"tag":265,"props":1383,"children":1384},{},[1385],{"type":79,"value":1386},"        return (f\"{year}-{month:02d}-{last_day}\", \"LAST\")\n",{"type":73,"tag":265,"props":1388,"children":1389},{"class":267,"line":456},[1390],{"type":73,"tag":265,"props":1391,"children":1392},{},[1393],{"type":79,"value":1394},"    return (dtc, None)\n",{"type":73,"tag":127,"props":1396,"children":1397},{},[],{"type":73,"tag":82,"props":1399,"children":1401},{"id":1400},"_6-aggregate-report-and-definexml",[1402],{"type":79,"value":1403},"6. Aggregate Report and define.xml",{"type":73,"tag":179,"props":1405,"children":1407},{"className":257,"code":1406,"language":259,"meta":188,"style":188},"from lxml import etree\n\ndef generate_validation_report(*issue_dfs, output_path=\"validation_report.csv\"):\n    \"\"\"Combine all issues into a single prioritized report.\"\"\"\n    all_issues = pd.concat([df for df in issue_dfs if not df.empty], ignore_index=True)\n    sev_order = {\"CRITICAL\": 0, \"HIGH\": 1, \"MEDIUM\": 2, \"LOW\": 3}\n    all_issues = all_issues.sort_values(by=\"SEVERITY\", key=lambda s: s.map(sev_order).fillna(4))\n    all_issues.to_csv(output_path, index=False)\n    for sev in [\"CRITICAL\", \"HIGH\", \"MEDIUM\", \"LOW\"]:\n        print(f\"  {sev}: {(all_issues['SEVERITY'] == sev).sum()}\")\n    return all_issues\n\ndef generate_define_xml(datasets: dict[str, pd.DataFrame], study_oid: str,\n                        output_path: str = \"define.xml\") -> None:\n    \"\"\"Generate minimal define.xml 2.0 skeleton from SDTM datasets.\"\"\"\n    NS = \"http:\u002F\u002Fwww.cdisc.org\u002Fns\u002Fodm\u002Fv1.3\"\n    DEF = \"http:\u002F\u002Fwww.cdisc.org\u002Fns\u002Fdef\u002Fv2.0\"\n    nsmap = {None: NS, \"def\": DEF, \"xlink\": \"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxlink\"}\n    root = etree.Element(\"ODM\", nsmap=nsmap, FileOID=f\"define_{study_oid}\",\n                          FileType=\"Snapshot\", ODMVersion=\"1.3.2\")\n    study = etree.SubElement(root, \"Study\", OID=study_oid)\n    gd = etree.SubElement(study, \"GlobalVariables\")\n    etree.SubElement(gd, \"StudyName\").text = study_oid\n    etree.SubElement(gd, \"ProtocolName\").text = study_oid\n    mdv = etree.SubElement(study, \"MetaDataVersion\", OID=\"MDV.1\", Name=\"SDTM Metadata\")\n    for domain_name, df in sorted(datasets.items()):\n        igd = etree.SubElement(mdv, \"ItemGroupDef\", OID=f\"IG.{domain_name}\",\n                               Name=domain_name, Repeating=\"Yes\" if domain_name != \"DM\" else \"No\")\n        for col in df.columns:\n            dtype = \"integer\" if df[col].dtype in (\"int64\", \"float64\") else \"text\"\n            etree.SubElement(mdv, \"ItemDef\", OID=f\"IT.{domain_name}.{col}\",\n                             Name=col, DataType=dtype)\n            etree.SubElement(igd, \"ItemRef\", ItemOID=f\"IT.{domain_name}.{col}\",\n                             Mandatory=\"Yes\" if col in (\"STUDYID\", \"USUBJID\") else \"No\")\n    etree.ElementTree(root).write(output_path, xml_declaration=True,\n                                   encoding=\"UTF-8\", pretty_print=True)\n",[1408],{"type":73,"tag":186,"props":1409,"children":1410},{"__ignoreMap":188},[1411,1419,1426,1434,1442,1450,1458,1466,1474,1482,1490,1498,1505,1513,1521,1529,1537,1545,1553,1561,1569,1577,1585,1593,1601,1609,1617,1625,1633,1641,1649,1657,1665,1673,1681,1689],{"type":73,"tag":265,"props":1412,"children":1413},{"class":267,"line":268},[1414],{"type":73,"tag":265,"props":1415,"children":1416},{},[1417],{"type":79,"value":1418},"from lxml import etree\n",{"type":73,"tag":265,"props":1420,"children":1421},{"class":267,"line":277},[1422],{"type":73,"tag":265,"props":1423,"children":1424},{"emptyLinePlaceholder":281},[1425],{"type":79,"value":284},{"type":73,"tag":265,"props":1427,"children":1428},{"class":267,"line":287},[1429],{"type":73,"tag":265,"props":1430,"children":1431},{},[1432],{"type":79,"value":1433},"def generate_validation_report(*issue_dfs, output_path=\"validation_report.csv\"):\n",{"type":73,"tag":265,"props":1435,"children":1436},{"class":267,"line":26},[1437],{"type":73,"tag":265,"props":1438,"children":1439},{},[1440],{"type":79,"value":1441},"    \"\"\"Combine all issues into a single prioritized report.\"\"\"\n",{"type":73,"tag":265,"props":1443,"children":1444},{"class":267,"line":304},[1445],{"type":73,"tag":265,"props":1446,"children":1447},{},[1448],{"type":79,"value":1449},"    all_issues = pd.concat([df for df in issue_dfs if not df.empty], ignore_index=True)\n",{"type":73,"tag":265,"props":1451,"children":1452},{"class":267,"line":313},[1453],{"type":73,"tag":265,"props":1454,"children":1455},{},[1456],{"type":79,"value":1457},"    sev_order = {\"CRITICAL\": 0, \"HIGH\": 1, \"MEDIUM\": 2, \"LOW\": 3}\n",{"type":73,"tag":265,"props":1459,"children":1460},{"class":267,"line":322},[1461],{"type":73,"tag":265,"props":1462,"children":1463},{},[1464],{"type":79,"value":1465},"    all_issues = all_issues.sort_values(by=\"SEVERITY\", key=lambda s: s.map(sev_order).fillna(4))\n",{"type":73,"tag":265,"props":1467,"children":1468},{"class":267,"line":331},[1469],{"type":73,"tag":265,"props":1470,"children":1471},{},[1472],{"type":79,"value":1473},"    all_issues.to_csv(output_path, index=False)\n",{"type":73,"tag":265,"props":1475,"children":1476},{"class":267,"line":340},[1477],{"type":73,"tag":265,"props":1478,"children":1479},{},[1480],{"type":79,"value":1481},"    for sev in [\"CRITICAL\", \"HIGH\", \"MEDIUM\", \"LOW\"]:\n",{"type":73,"tag":265,"props":1483,"children":1484},{"class":267,"line":349},[1485],{"type":73,"tag":265,"props":1486,"children":1487},{},[1488],{"type":79,"value":1489},"        print(f\"  {sev}: {(all_issues['SEVERITY'] == sev).sum()}\")\n",{"type":73,"tag":265,"props":1491,"children":1492},{"class":267,"line":358},[1493],{"type":73,"tag":265,"props":1494,"children":1495},{},[1496],{"type":79,"value":1497},"    return all_issues\n",{"type":73,"tag":265,"props":1499,"children":1500},{"class":267,"line":367},[1501],{"type":73,"tag":265,"props":1502,"children":1503},{"emptyLinePlaceholder":281},[1504],{"type":79,"value":284},{"type":73,"tag":265,"props":1506,"children":1507},{"class":267,"line":375},[1508],{"type":73,"tag":265,"props":1509,"children":1510},{},[1511],{"type":79,"value":1512},"def generate_define_xml(datasets: dict[str, pd.DataFrame], study_oid: str,\n",{"type":73,"tag":265,"props":1514,"children":1515},{"class":267,"line":384},[1516],{"type":73,"tag":265,"props":1517,"children":1518},{},[1519],{"type":79,"value":1520},"                        output_path: str = \"define.xml\") -> None:\n",{"type":73,"tag":265,"props":1522,"children":1523},{"class":267,"line":393},[1524],{"type":73,"tag":265,"props":1525,"children":1526},{},[1527],{"type":79,"value":1528},"    \"\"\"Generate minimal define.xml 2.0 skeleton from SDTM datasets.\"\"\"\n",{"type":73,"tag":265,"props":1530,"children":1531},{"class":267,"line":402},[1532],{"type":73,"tag":265,"props":1533,"children":1534},{},[1535],{"type":79,"value":1536},"    NS = \"http:\u002F\u002Fwww.cdisc.org\u002Fns\u002Fodm\u002Fv1.3\"\n",{"type":73,"tag":265,"props":1538,"children":1539},{"class":267,"line":411},[1540],{"type":73,"tag":265,"props":1541,"children":1542},{},[1543],{"type":79,"value":1544},"    DEF = \"http:\u002F\u002Fwww.cdisc.org\u002Fns\u002Fdef\u002Fv2.0\"\n",{"type":73,"tag":265,"props":1546,"children":1547},{"class":267,"line":420},[1548],{"type":73,"tag":265,"props":1549,"children":1550},{},[1551],{"type":79,"value":1552},"    nsmap = {None: NS, \"def\": DEF, \"xlink\": \"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxlink\"}\n",{"type":73,"tag":265,"props":1554,"children":1555},{"class":267,"line":429},[1556],{"type":73,"tag":265,"props":1557,"children":1558},{},[1559],{"type":79,"value":1560},"    root = etree.Element(\"ODM\", nsmap=nsmap, FileOID=f\"define_{study_oid}\",\n",{"type":73,"tag":265,"props":1562,"children":1563},{"class":267,"line":438},[1564],{"type":73,"tag":265,"props":1565,"children":1566},{},[1567],{"type":79,"value":1568},"                          FileType=\"Snapshot\", ODMVersion=\"1.3.2\")\n",{"type":73,"tag":265,"props":1570,"children":1571},{"class":267,"line":447},[1572],{"type":73,"tag":265,"props":1573,"children":1574},{},[1575],{"type":79,"value":1576},"    study = etree.SubElement(root, \"Study\", OID=study_oid)\n",{"type":73,"tag":265,"props":1578,"children":1579},{"class":267,"line":456},[1580],{"type":73,"tag":265,"props":1581,"children":1582},{},[1583],{"type":79,"value":1584},"    gd = etree.SubElement(study, \"GlobalVariables\")\n",{"type":73,"tag":265,"props":1586,"children":1587},{"class":267,"line":464},[1588],{"type":73,"tag":265,"props":1589,"children":1590},{},[1591],{"type":79,"value":1592},"    etree.SubElement(gd, \"StudyName\").text = study_oid\n",{"type":73,"tag":265,"props":1594,"children":1595},{"class":267,"line":472},[1596],{"type":73,"tag":265,"props":1597,"children":1598},{},[1599],{"type":79,"value":1600},"    etree.SubElement(gd, \"ProtocolName\").text = study_oid\n",{"type":73,"tag":265,"props":1602,"children":1603},{"class":267,"line":481},[1604],{"type":73,"tag":265,"props":1605,"children":1606},{},[1607],{"type":79,"value":1608},"    mdv = etree.SubElement(study, \"MetaDataVersion\", OID=\"MDV.1\", Name=\"SDTM Metadata\")\n",{"type":73,"tag":265,"props":1610,"children":1611},{"class":267,"line":490},[1612],{"type":73,"tag":265,"props":1613,"children":1614},{},[1615],{"type":79,"value":1616},"    for domain_name, df in sorted(datasets.items()):\n",{"type":73,"tag":265,"props":1618,"children":1619},{"class":267,"line":498},[1620],{"type":73,"tag":265,"props":1621,"children":1622},{},[1623],{"type":79,"value":1624},"        igd = etree.SubElement(mdv, \"ItemGroupDef\", OID=f\"IG.{domain_name}\",\n",{"type":73,"tag":265,"props":1626,"children":1627},{"class":267,"line":507},[1628],{"type":73,"tag":265,"props":1629,"children":1630},{},[1631],{"type":79,"value":1632},"                               Name=domain_name, Repeating=\"Yes\" if domain_name != \"DM\" else \"No\")\n",{"type":73,"tag":265,"props":1634,"children":1635},{"class":267,"line":516},[1636],{"type":73,"tag":265,"props":1637,"children":1638},{},[1639],{"type":79,"value":1640},"        for col in df.columns:\n",{"type":73,"tag":265,"props":1642,"children":1643},{"class":267,"line":524},[1644],{"type":73,"tag":265,"props":1645,"children":1646},{},[1647],{"type":79,"value":1648},"            dtype = \"integer\" if df[col].dtype in (\"int64\", \"float64\") else \"text\"\n",{"type":73,"tag":265,"props":1650,"children":1651},{"class":267,"line":533},[1652],{"type":73,"tag":265,"props":1653,"children":1654},{},[1655],{"type":79,"value":1656},"            etree.SubElement(mdv, \"ItemDef\", OID=f\"IT.{domain_name}.{col}\",\n",{"type":73,"tag":265,"props":1658,"children":1659},{"class":267,"line":542},[1660],{"type":73,"tag":265,"props":1661,"children":1662},{},[1663],{"type":79,"value":1664},"                             Name=col, DataType=dtype)\n",{"type":73,"tag":265,"props":1666,"children":1667},{"class":267,"line":551},[1668],{"type":73,"tag":265,"props":1669,"children":1670},{},[1671],{"type":79,"value":1672},"            etree.SubElement(igd, \"ItemRef\", ItemOID=f\"IT.{domain_name}.{col}\",\n",{"type":73,"tag":265,"props":1674,"children":1675},{"class":267,"line":560},[1676],{"type":73,"tag":265,"props":1677,"children":1678},{},[1679],{"type":79,"value":1680},"                             Mandatory=\"Yes\" if col in (\"STUDYID\", \"USUBJID\") else \"No\")\n",{"type":73,"tag":265,"props":1682,"children":1683},{"class":267,"line":569},[1684],{"type":73,"tag":265,"props":1685,"children":1686},{},[1687],{"type":79,"value":1688},"    etree.ElementTree(root).write(output_path, xml_declaration=True,\n",{"type":73,"tag":265,"props":1690,"children":1691},{"class":267,"line":578},[1692],{"type":73,"tag":265,"props":1693,"children":1694},{},[1695],{"type":79,"value":1696},"                                   encoding=\"UTF-8\", pretty_print=True)\n",{"type":73,"tag":127,"props":1698,"children":1699},{},[],{"type":73,"tag":82,"props":1701,"children":1703},{"id":1702},"_7-parameter-reference",[1704],{"type":79,"value":1705},"7. Parameter Reference",{"type":73,"tag":1707,"props":1708,"children":1709},"table",{},[1710,1734],{"type":73,"tag":1711,"props":1712,"children":1713},"thead",{},[1714],{"type":73,"tag":1715,"props":1716,"children":1717},"tr",{},[1718,1724,1729],{"type":73,"tag":1719,"props":1720,"children":1721},"th",{},[1722],{"type":79,"value":1723},"Parameter",{"type":73,"tag":1719,"props":1725,"children":1726},{},[1727],{"type":79,"value":1728},"Default",{"type":73,"tag":1719,"props":1730,"children":1731},{},[1732],{"type":79,"value":1733},"Notes",{"type":73,"tag":1735,"props":1736,"children":1737},"tbody",{},[1738,1757,1775,1793,1811,1829,1847,1865],{"type":73,"tag":1715,"props":1739,"children":1740},{},[1741,1747,1752],{"type":73,"tag":1742,"props":1743,"children":1744},"td",{},[1745],{"type":79,"value":1746},"VS SYSBP range",{"type":73,"tag":1742,"props":1748,"children":1749},{},[1750],{"type":79,"value":1751},"60–250 mmHg",{"type":73,"tag":1742,"props":1753,"children":1754},{},[1755],{"type":79,"value":1756},"Pediatric: ×0.7 low, ×0.8 high",{"type":73,"tag":1715,"props":1758,"children":1759},{},[1760,1765,1770],{"type":73,"tag":1742,"props":1761,"children":1762},{},[1763],{"type":79,"value":1764},"VS HR range",{"type":73,"tag":1742,"props":1766,"children":1767},{},[1768],{"type":79,"value":1769},"30–220 beats\u002Fmin",{"type":73,"tag":1742,"props":1771,"children":1772},{},[1773],{"type":79,"value":1774},"Pediatric: ×1.3 high; Geriatric: flag >180 as CRITICAL",{"type":73,"tag":1715,"props":1776,"children":1777},{},[1778,1783,1788],{"type":73,"tag":1742,"props":1779,"children":1780},{},[1781],{"type":79,"value":1782},"LB ALT\u002FAST",{"type":73,"tag":1742,"props":1784,"children":1785},{},[1786],{"type":79,"value":1787},"0–500 U\u002FL",{"type":73,"tag":1742,"props":1789,"children":1790},{},[1791],{"type":79,"value":1792},"Flag >3×ULN (ALT ULN=40, AST ULN=37) for Hy's Law",{"type":73,"tag":1715,"props":1794,"children":1795},{},[1796,1801,1806],{"type":73,"tag":1742,"props":1797,"children":1798},{},[1799],{"type":79,"value":1800},"LB BILI",{"type":73,"tag":1742,"props":1802,"children":1803},{},[1804],{"type":79,"value":1805},"0–30 mg\u002FdL",{"type":73,"tag":1742,"props":1807,"children":1808},{},[1809],{"type":79,"value":1810},"Hy's Law: >2×ULN (ULN=1.2) with elevated ALT\u002FAST",{"type":73,"tag":1715,"props":1812,"children":1813},{},[1814,1819,1824],{"type":73,"tag":1742,"props":1815,"children":1816},{},[1817],{"type":79,"value":1818},"QTcF (cardiology)",{"type":73,"tag":1742,"props":1820,"children":1821},{},[1822],{"type":79,"value":1823},"\u003C450 normal",{"type":73,"tag":1742,"props":1825,"children":1826},{},[1827],{"type":79,"value":1828},"450–480 borderline, >480 prolonged, >500 CRITICAL",{"type":73,"tag":1715,"props":1830,"children":1831},{},[1832,1837,1842],{"type":73,"tag":1742,"props":1833,"children":1834},{},[1835],{"type":79,"value":1836},"RECIST (oncology)",{"type":73,"tag":1742,"props":1838,"children":1839},{},[1840],{"type":79,"value":1841},"CR\u002FPR\u002FSD\u002FPD\u002FNE",{"type":73,"tag":1742,"props":1843,"children":1844},{},[1845],{"type":79,"value":1846},"Flag any other value as CT violation",{"type":73,"tag":1715,"props":1848,"children":1849},{},[1850,1855,1860],{"type":73,"tag":1742,"props":1851,"children":1852},{},[1853],{"type":79,"value":1854},"Partial date imputation",{"type":73,"tag":1742,"props":1856,"children":1857},{},[1858],{"type":79,"value":1859},"Start→earliest, End→latest",{"type":73,"tag":1742,"props":1861,"children":1862},{},[1863],{"type":79,"value":1864},"Always set DTYPE column",{"type":73,"tag":1715,"props":1866,"children":1867},{},[1868,1873,1878],{"type":73,"tag":1742,"props":1869,"children":1870},{},[1871],{"type":79,"value":1872},"Severity levels",{"type":73,"tag":1742,"props":1874,"children":1875},{},[1876],{"type":79,"value":1877},"CRITICAL\u002FHIGH\u002FMEDIUM\u002FLOW",{"type":73,"tag":1742,"props":1879,"children":1880},{},[1881],{"type":79,"value":1882},"Map to query priority P1–P4",{"type":73,"tag":127,"props":1884,"children":1885},{},[],{"type":73,"tag":82,"props":1887,"children":1889},{"id":1888},"common-mistakes",[1890],{"type":79,"value":1891},"Common Mistakes",{"type":73,"tag":101,"props":1893,"children":1894},{},[1895,1919,1940,1961,1982,2003],{"type":73,"tag":105,"props":1896,"children":1897},{},[1898,1903,1905,1910,1912,1917],{"type":73,"tag":195,"props":1899,"children":1900},{},[1901],{"type":79,"value":1902},"Wrong:",{"type":79,"value":1904}," Applying adult vital sign ranges to pediatric subjects without age adjustment\n",{"type":73,"tag":195,"props":1906,"children":1907},{},[1908],{"type":79,"value":1909},"Right:",{"type":79,"value":1911}," Scale ranges by age group (e.g., pediatric SBP upper = adult × 0.8, HR upper = adult × 1.3)\n",{"type":73,"tag":195,"props":1913,"children":1914},{},[1915],{"type":79,"value":1916},"Why:",{"type":79,"value":1918}," Normal pediatric values differ substantially from adults — flagging a child's HR of 130 as abnormal wastes data management time",{"type":73,"tag":105,"props":1920,"children":1921},{},[1922,1926,1928,1932,1934,1938],{"type":73,"tag":195,"props":1923,"children":1924},{},[1925],{"type":79,"value":1902},{"type":79,"value":1927}," Flagging partial dates as errors instead of applying imputation rules\n",{"type":73,"tag":195,"props":1929,"children":1930},{},[1931],{"type":79,"value":1909},{"type":79,"value":1933}," Impute start dates to earliest possible (01-JAN-YYYY) and end dates to latest possible, then set DTYPE = \"DERIVED\"\n",{"type":73,"tag":195,"props":1935,"children":1936},{},[1937],{"type":79,"value":1916},{"type":79,"value":1939}," Partial dates are expected in clinical data; rejecting them loses valid records and violates SDTM imputation conventions",{"type":73,"tag":105,"props":1941,"children":1942},{},[1943,1947,1949,1953,1955,1959],{"type":73,"tag":195,"props":1944,"children":1945},{},[1946],{"type":79,"value":1902},{"type":79,"value":1948}," Validating SDTM controlled terminology against a hardcoded list instead of the study's CT version\n",{"type":73,"tag":195,"props":1950,"children":1951},{},[1952],{"type":79,"value":1909},{"type":79,"value":1954}," Always validate against the specific CDISC CT version declared in the study's define.xml\n",{"type":73,"tag":195,"props":1956,"children":1957},{},[1958],{"type":79,"value":1916},{"type":79,"value":1960}," CT evolves across versions — a valid term in CT 2023-12-15 may not exist in CT 2022-09-30 and vice versa",{"type":73,"tag":105,"props":1962,"children":1963},{},[1964,1968,1970,1974,1976,1980],{"type":73,"tag":195,"props":1965,"children":1966},{},[1967],{"type":79,"value":1902},{"type":79,"value":1969}," Running cross-form date checks without accounting for time zones or visit windows\n",{"type":73,"tag":195,"props":1971,"children":1972},{},[1973],{"type":79,"value":1909},{"type":79,"value":1975}," Allow ±1 day tolerance for cross-form date comparisons and document the tolerance in the validation plan\n",{"type":73,"tag":195,"props":1977,"children":1978},{},[1979],{"type":79,"value":1916},{"type":79,"value":1981}," Subjects crossing time zones or overnight visits produce legitimate 1-day discrepancies that are not data errors",{"type":73,"tag":105,"props":1983,"children":1984},{},[1985,1989,1991,1995,1997,2001],{"type":73,"tag":195,"props":1986,"children":1987},{},[1988],{"type":79,"value":1902},{"type":79,"value":1990}," Generating queries for every out-of-range lab value without checking units\n",{"type":73,"tag":195,"props":1992,"children":1993},{},[1994],{"type":79,"value":1909},{"type":79,"value":1996}," Normalize units before range checking (e.g., convert mg\u002FdL ↔ µmol\u002FL for creatinine) using LBORRESU\u002FLBSTRESU\n",{"type":73,"tag":195,"props":1998,"children":1999},{},[2000],{"type":79,"value":1916},{"type":79,"value":2002}," A creatinine of 88 µmol\u002FL is normal but flags as critical if the range check assumes mg\u002FdL (normal: 0.6–1.2)",{"type":73,"tag":105,"props":2004,"children":2005},{},[2006,2010,2012,2016,2018,2022],{"type":73,"tag":195,"props":2007,"children":2008},{},[2009],{"type":79,"value":1902},{"type":79,"value":2011}," Treating all validation findings as equal priority\n",{"type":73,"tag":195,"props":2013,"children":2014},{},[2015],{"type":79,"value":1909},{"type":79,"value":2017}," Classify by clinical impact: CRITICAL (patient safety), HIGH (primary endpoint), MEDIUM (secondary), LOW (cosmetic)\n",{"type":73,"tag":195,"props":2019,"children":2020},{},[2021],{"type":79,"value":1916},{"type":79,"value":2023}," Flooding sites with low-priority queries delays resolution of safety-critical issues",{"type":73,"tag":2025,"props":2026,"children":2027},"style",{},[2028],{"type":79,"value":2029},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2031,"total":2211},[2032,2053,2074,2084,2097,2110,2120,2130,2151,2166,2181,2196],{"slug":2033,"name":2033,"fn":2034,"description":2035,"org":2036,"tags":2037,"stars":2050,"repoUrl":2051,"updatedAt":2052},"agentcore-investigation","investigate Bedrock AgentCore runtime sessions","Investigate Bedrock AgentCore runtime sessions via CloudWatch Logs Insights — resolve session\u002Ftrace IDs, query OTEL spans, filter noise, build timelines. Use when debugging AgentCore agent sessions, tracing tool calls, or analyzing latency.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2038,2041,2044,2047],{"name":2039,"slug":2040,"type":16},"AWS","aws",{"name":2042,"slug":2043,"type":16},"Debugging","debugging",{"name":2045,"slug":2046,"type":16},"Logs","logs",{"name":2048,"slug":2049,"type":16},"Observability","observability",9427,"https:\u002F\u002Fgithub.com\u002Fawslabs\u002Fmcp","2026-07-12T08:37:22.601527",{"slug":2054,"name":2055,"fn":2056,"description":2057,"org":2058,"tags":2059,"stars":2050,"repoUrl":2051,"updatedAt":2073},"amazon-aurora-dsql","amazon aurora dsql","build applications with Aurora DSQL","Build with Aurora DSQL — manage schemas, execute queries, handle migrations, diagnose query plans, load data, and develop applications with a serverless, distributed SQL database. Covers IAM auth, multi-tenant patterns, MySQL-to-DSQL and PostgreSQL-to-DSQL schema conversion, FK replacement code generation, OCC retry patterns, ORM migration (Django\u002FHibernate\u002FRails), DDL operations, query plan explainability, SQL compatibility validation, and bulk data loading. Triggers on phrases like: DSQL, Aurora DSQL, create DSQL table, DSQL schema, migrate to DSQL, distributed SQL database, serverless PostgreSQL-compatible database, DSQL query plan, DSQL EXPLAIN ANALYZE, why is my DSQL query slow, DSQL foreign key, DSQL OCC retry, DSQL multi-region, load into DSQL, load CSV into DSQL, bulk load DSQL, aurora-dsql-loader.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2060,2063,2064,2067,2070],{"name":2061,"slug":2062,"type":16},"Aurora","aurora",{"name":2039,"slug":2040,"type":16},{"name":2065,"slug":2066,"type":16},"Database","database",{"name":2068,"slug":2069,"type":16},"Serverless","serverless",{"name":2071,"slug":2072,"type":16},"SQL","sql","2026-07-12T08:36:45.053393",{"slug":2075,"name":2076,"fn":2056,"description":2057,"org":2077,"tags":2078,"stars":2050,"repoUrl":2051,"updatedAt":2083},"aurora-dsql","aurora dsql",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2079,2080,2081,2082],{"name":2039,"slug":2040,"type":16},{"name":2065,"slug":2066,"type":16},{"name":2068,"slug":2069,"type":16},{"name":2071,"slug":2072,"type":16},"2026-07-12T08:36:42.694299",{"slug":2085,"name":2086,"fn":2056,"description":2057,"org":2087,"tags":2088,"stars":2050,"repoUrl":2051,"updatedAt":2096},"aws-dsql","aws dsql",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2089,2090,2091,2094,2095],{"name":2039,"slug":2040,"type":16},{"name":2065,"slug":2066,"type":16},{"name":2092,"slug":2093,"type":16},"Migration","migration",{"name":2068,"slug":2069,"type":16},{"name":2071,"slug":2072,"type":16},"2026-07-12T08:36:38.584057",{"slug":2098,"name":2099,"fn":2056,"description":2057,"org":2100,"tags":2101,"stars":2050,"repoUrl":2051,"updatedAt":2109},"distributed-postgres","distributed postgres",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2102,2103,2104,2107,2108],{"name":2039,"slug":2040,"type":16},{"name":2065,"slug":2066,"type":16},{"name":2105,"slug":2106,"type":16},"PostgreSQL","postgresql",{"name":2068,"slug":2069,"type":16},{"name":2071,"slug":2072,"type":16},"2026-07-12T08:36:46.530743",{"slug":2111,"name":2112,"fn":2056,"description":2057,"org":2113,"tags":2114,"stars":2050,"repoUrl":2051,"updatedAt":2119},"distributed-sql","distributed sql",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2115,2116,2117,2118],{"name":2039,"slug":2040,"type":16},{"name":2065,"slug":2066,"type":16},{"name":2068,"slug":2069,"type":16},{"name":2071,"slug":2072,"type":16},"2026-07-12T08:36:48.104182",{"slug":2121,"name":2121,"fn":2056,"description":2057,"org":2122,"tags":2123,"stars":2050,"repoUrl":2051,"updatedAt":2129},"dsql",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2124,2125,2126,2127,2128],{"name":2039,"slug":2040,"type":16},{"name":2065,"slug":2066,"type":16},{"name":2092,"slug":2093,"type":16},{"name":2068,"slug":2069,"type":16},{"name":2071,"slug":2072,"type":16},"2026-07-12T08:36:36.374512",{"slug":2131,"name":2131,"fn":2132,"description":2133,"org":2134,"tags":2135,"stars":2148,"repoUrl":2149,"updatedAt":2150},"cost-efficiency-analyzer","analyze cost efficiency and expenses","Analyzes cost structure, cost efficiency, and expense management from P&L data. Use when the user asks about costs, expenses, COGS, operating expenses, cost ratios, cost control, spending efficiency, margin compression from cost side, or wants to understand where money is going. Also use for \"are we spending too much\", \"cost breakdown\", \"expense analysis\", or \"how efficient are our operations\". NOT for revenue or top-line analysis.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2136,2139,2142,2145],{"name":2137,"slug":2138,"type":16},"Accounting","accounting",{"name":2140,"slug":2141,"type":16},"Analytics","analytics",{"name":2143,"slug":2144,"type":16},"Cost Optimization","cost-optimization",{"name":2146,"slug":2147,"type":16},"Finance","finance",3176,"https:\u002F\u002Fgithub.com\u002Fawslabs\u002Fagentcore-samples","2026-07-12T08:40:03.29555",{"slug":2152,"name":2152,"fn":2153,"description":2154,"org":2155,"tags":2156,"stars":2148,"repoUrl":2149,"updatedAt":2165},"executive-financial-briefing","generate executive financial briefings","Generates a concise executive-level financial briefing or summary suitable for a CEO, CFO, or board presentation. Use when the user asks for a summary, briefing, executive summary, board update, financial overview, financial health check, or \"how is the business doing\". Covers the full P&L picture in one page. Also use for \"give me the highlights\", \"what do I need to know\", or \"quick financial update\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2157,2158,2159,2162],{"name":2039,"slug":2040,"type":16},{"name":2146,"slug":2147,"type":16},{"name":2160,"slug":2161,"type":16},"Management","management",{"name":2163,"slug":2164,"type":16},"Reporting","reporting","2026-07-12T08:40:02.066471",{"slug":2167,"name":2167,"fn":2168,"description":2169,"org":2170,"tags":2171,"stars":2148,"repoUrl":2149,"updatedAt":2180},"multi-quarter-trend-analysis","analyze multi-quarter financial trends","Analyzes financial trends across multiple quarters by comparing P&L metrics over time. Use when the user wants to see trends, patterns, trajectories, or directional movement across 3 or more quarters. Also use for \"how are we trending\", \"show me the trend\", \"track performance over time\", \"quarter over quarter comparison across all quarters\", or any multi-period longitudinal analysis.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2172,2173,2174,2177],{"name":2140,"slug":2141,"type":16},{"name":2146,"slug":2147,"type":16},{"name":2175,"slug":2176,"type":16},"Financial Statements","financial-statements",{"name":2178,"slug":2179,"type":16},"Variance Analysis","variance-analysis","2026-07-12T08:40:00.79141",{"slug":2182,"name":2182,"fn":2183,"description":2184,"org":2185,"tags":2186,"stars":2148,"repoUrl":2149,"updatedAt":2195},"pdf","process and manipulate PDF documents","Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text\u002Ftables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting\u002Fdecrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2187,2190,2193],{"name":2188,"slug":2189,"type":16},"Automation","automation",{"name":2191,"slug":2192,"type":16},"Documents","documents",{"name":2194,"slug":2182,"type":16},"PDF","2026-07-12T08:41:44.135656",{"slug":2197,"name":2197,"fn":2198,"description":2199,"org":2200,"tags":2201,"stars":2148,"repoUrl":2149,"updatedAt":2210},"quarterly-kpi-calculator","calculate quarterly financial KPIs","Calculates quarterly financial KPIs from P&L data. P&L figures can be provided directly by the user or fetched from the financial data MCP server. Use when the user wants KPI calculations such as Gross Margin %, EBITDA Margin %, Operating Expense Ratio, or Revenue Growth % QoQ. Also use for quarterly performance review, P&L analysis, or interpreting financial ratios against benchmarks.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2202,2203,2206,2207],{"name":2137,"slug":2138,"type":16},{"name":2204,"slug":2205,"type":16},"Data Analysis","data-analysis",{"name":2146,"slug":2147,"type":16},{"name":2208,"slug":2209,"type":16},"KPI","kpi","2026-07-12T08:39:59.54971",150,{"items":2213,"total":614},[2214,2231,2246,2258,2271,2284,2297],{"slug":2215,"name":2215,"fn":2216,"description":2217,"org":2218,"tags":2219,"stars":26,"repoUrl":27,"updatedAt":2230},"aws-genai-ml-architect","design AWS GenAI and ML architectures","Reasoning skill for designing AWS GenAI and ML architectures for healthcare and life sciences workloads. Use when the user asks to choose between SageMaker and Bedrock, design a RAG system over medical literature, architect clinical NLP or medical imaging inference, plan genomics or drug discovery pipelines on AWS, address HIPAA\u002FPHI compliance in ML systems, design MLOps for regulated clinical models, or optimize cost for HCLS ML workloads. Triggers include \"AWS architecture\", \"SageMaker vs Bedrock\", \"HIPAA ML\", \"clinical RAG\", \"medical imaging inference\", \"genomics on AWS\", \"PHI training\", \"MLOps healthcare\", \"Bedrock guardrails\", \"HealthLake\", \"HCLS cloud architecture\", \"BAA compliance\", \"SageMaker endpoint\", \"Bedrock knowledge base\", \"clinical NLP on AWS\", \"FDA SaMD on AWS\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2220,2223,2224,2225,2227],{"name":2221,"slug":2222,"type":16},"Architecture","architecture",{"name":2039,"slug":2040,"type":16},{"name":18,"slug":19,"type":16},{"name":2226,"slug":41,"type":16},"Life Sciences",{"name":2228,"slug":2229,"type":16},"LLM","llm","2026-07-12T08:38:07.975937",{"slug":2232,"name":2232,"fn":2233,"description":2234,"org":2235,"tags":2236,"stars":26,"repoUrl":27,"updatedAt":2245},"biomarker-discovery","guide biomarker discovery and validation","Reason about biomarker discovery and validation in HCLS — classifying biomarker intent, choosing feature-selection and cross-validation strategies, avoiding leakage, and planning external replication. Use when the user asks to discover, develop, or validate a biomarker; select features from high-dimensional omics or clinical data; design a validation study; choose evaluation metrics; justify sample size; combine multi-omics signals; or assess clinical utility. Triggers include \"discover a biomarker\", \"validate biomarker\", \"prognostic vs predictive\", \"feature selection\", \"LASSO vs elastic net\", \"nested cross-validation\", \"data leakage\", \"C-index\", \"time-dependent AUC\", \"decision curve analysis\", \"external validation cohort\", \"events per variable\", \"optimism-corrected\", \"multi-omics integration\", \"clinical utility of a biomarker\", \"is this biomarker ready\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2237,2238,2241,2242],{"name":2039,"slug":2040,"type":16},{"name":2239,"slug":2240,"type":16},"Bioinformatics","bioinformatics",{"name":2226,"slug":41,"type":16},{"name":2243,"slug":2244,"type":16},"Research","research","2026-07-12T08:37:49.295301",{"slug":2247,"name":2247,"fn":2248,"description":2249,"org":2250,"tags":2251,"stars":26,"repoUrl":27,"updatedAt":2257},"cdisc-compliance","reason about CDISC SDTM and ADaM implementation","Reason about CDISC SDTM and ADaM implementation for regulatory submissions. Use when the user asks about SDTM domain mapping, ADaM dataset design, controlled terminology versioning, define.xml completeness, FDA or PMDA submission requirements, query prioritization by clinical impact, SUPPQUAL usage, or CDISC compliance review. Triggers include \"SDTM mapping\", \"ADaM dataset\", \"CDISC compliance\", \"controlled terminology\", \"define.xml\", \"FDA submission data\", \"PMDA submission\", \"SDTM domain\", \"ADSL\", \"ADAE\", \"ADLB\", \"BDS structure\", \"SUPPQUAL\", \"RELREC\", \"value-level metadata\", \"CDISC CT\", \"regulatory submission data standards\", \"eCTD datasets\", \"SDTM 3.3\", \"ADaM 1.1\", \"query prioritization\", \"clinical data review\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2252,2253,2254],{"name":24,"slug":25,"type":16},{"name":2226,"slug":41,"type":16},{"name":2255,"slug":2256,"type":16},"Regulatory Compliance","regulatory-compliance","2026-07-12T08:37:33.35594",{"slug":2259,"name":2259,"fn":2260,"description":2261,"org":2262,"tags":2263,"stars":26,"repoUrl":27,"updatedAt":2270},"cell-type-annotation","annotate single-cell RNA-seq clusters","Generate code to assign cell type labels to single-cell RNA-seq clusters using CellTypist, SingleR, marker-based annotation, or reference label transfer (scANVI\u002Fingest). Triggers on requests to \"annotate cell types\", \"label clusters\", \"run CellTypist\", \"SingleR annotation\", \"marker gene dotplot\", \"transfer labels from reference atlas\", \"cell identity\", \"automated annotation\", \"reference mapping\", \"scANVI label transfer\", \"canonical markers\", \"immune cell types\", \"hierarchical annotation\", \"majority voting CellTypist\", \"over-clustering annotation\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2264,2265,2266,2267],{"name":2239,"slug":2240,"type":16},{"name":2204,"slug":2205,"type":16},{"name":2226,"slug":41,"type":16},{"name":2268,"slug":2269,"type":16},"RNA-seq","rna-seq","2026-07-12T08:38:05.443454",{"slug":2272,"name":2272,"fn":2273,"description":2274,"org":2275,"tags":2276,"stars":26,"repoUrl":27,"updatedAt":2283},"cheminformatics","calculate molecular properties with RDKit","Cheminformatics pipeline for small-molecule property calculation, filtering, and similarity analysis using RDKit. Use when the user asks to compute molecular descriptors, filter compounds by Lipinski or Veber rules, detect PAINS, calculate fingerprint similarity, run matched molecular pair analysis, generate ADMET descriptors, or process SMILES. Triggers include \"RDKit\", \"molecular descriptors\", \"Lipinski\", \"rule of five\", \"Veber\", \"PAINS\", \"pan-assay interference\", \"Morgan fingerprint\", \"Tanimoto\", \"fingerprint similarity\", \"matched molecular pair\", \"MMP\", \"mmpdb\", \"ADMET\", \"druglikeness\", \"SMILES\", \"cheminformatics\", \"compound filtering\", \"chemical similarity\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2277,2278,2281,2282],{"name":2239,"slug":2240,"type":16},{"name":2279,"slug":2280,"type":16},"Chemistry","chemistry",{"name":2204,"slug":2205,"type":16},{"name":2243,"slug":2244,"type":16},"2026-07-12T08:37:28.334619",{"slug":2285,"name":2285,"fn":2286,"description":2287,"org":2288,"tags":2289,"stars":26,"repoUrl":27,"updatedAt":2296},"claims-analytics","analyze and parse healthcare claims data","Pipeline skill for healthcare claims data parsing, analysis, and fraud detection. Use when the user asks to parse X12 837 or 835 claim files, manipulate ICD-10 CPT or HCPCS codes, detect billing pattern anomalies, profile providers against specialty peers, identify outlier billing behavior, validate NCCI edits programmatically, detect duplicate claims, run Benford's law analysis on charges, build claims data pipelines, or analyze E&M code distributions. Triggers include \"parse X12 837\", \"parse 835\", \"claims SQL\", \"ICD-10 manipulation\", \"CPT code analysis\", \"provider profiling\", \"billing outlier\", \"NCCI validation code\", \"duplicate claim detection\", \"Benford's law charges\", \"claims ETL\", \"E&M distribution analysis\", \"claims analytics pipeline\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2290,2291,2292,2295],{"name":2204,"slug":2205,"type":16},{"name":18,"slug":19,"type":16},{"name":2293,"slug":2294,"type":16},"Insurance","insurance",{"name":2226,"slug":41,"type":16},"2026-07-12T08:37:34.815088",{"slug":2298,"name":2298,"fn":2299,"description":2300,"org":2301,"tags":2302,"stars":26,"repoUrl":27,"updatedAt":2306},"claims-billing-rules","analyze healthcare claims billing rules","Reasoning skill for healthcare claims billing rules and fraud detection logic. Use when the user asks about CMS billing rules, place of service codes, global surgery periods, modifier usage (25 59 76 77), NCCI edit logic, column 1 column 2 code pairs, mutually exclusive procedures, modifier indicators, fraud waste and abuse patterns, E&M upcoding, unbundling, phantom billing, impossible day detection, coding error versus fraud distinction, FWA investigation methodology, or claims audit logic. Triggers include \"CMS billing rules\", \"NCCI edits\", \"modifier 25\", \"modifier 59\", \"global surgery period\", \"upcoding\", \"unbundling\", \"phantom billing\", \"impossible day\", \"FWA\", \"fraud waste abuse\", \"coding error vs fraud\", \"claims audit\", \"billing compliance\", \"E&M level selection\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2303,2304,2305],{"name":18,"slug":19,"type":16},{"name":2293,"slug":2294,"type":16},{"name":2255,"slug":2256,"type":16},"2026-07-12T08:38:28.210856"]