[
  {
    "id": "e5f1fe58-b919-47b1-94b9-edc95fe25ffb",
    "sort_order": 0,
    "rubric_text": "In file `audit_log.xlsx` in folder `Rodriguez_07221975`, there must be exactly 2 rows of data (excluding headers).",
    "verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n    folder = Path(workspace_path) / 'Rodriguez_07221975'\n    if not folder.exists():\n        return {'pass': False, 'score': 0.0, 'feedback': 'Folder Rodriguez_07221975 not found in workspace'}\n    \n    audit_file = folder / 'audit_log.xlsx'\n    if not audit_file.exists():\n        return {'pass': False, 'score': 0.0, 'feedback': 'audit_log.xlsx not found in Rodriguez_07221975 folder'}\n    \n    try:\n        wb = openpyxl.load_workbook(audit_file)\n        ws = wb.active\n        # Count non-empty rows excluding header\n        data_rows = []\n        for row in ws.iter_rows(min_row=2, values_only=True):\n            if any(cell is not None for cell in row):\n                data_rows.append(row)\n        \n        if len(data_rows) == 2:\n            return {'pass': True, 'score': 1.0, 'feedback': f'audit_log.xlsx in Rodriguez_07221975 has exactly 2 data rows as expected.'}\n        else:\n            return {'pass': False, 'score': 0.0, 'feedback': f'audit_log.xlsx in Rodriguez_07221975 has {len(data_rows)} data rows, expected exactly 2.'}\n    except Exception as e:\n        return {'pass': False, 'score': 0.0, 'feedback': f'Error reading audit_log.xlsx: {e}'}\n",
    "criterion_type": "expected_output"
  },
  {
    "id": "a10085f6-f540-40ad-9068-d6722d443148",
    "sort_order": 1,
    "rubric_text": "In `slack_data.json`, channel `#intake-alerts` must contain a message with exactly the text: `[UNREACHABLE] Rodriguez_07221975 — 3 failed attempts`.",
    "verifier_code": "from pathlib import Path\nimport json\nimport string\n\ndef verify(workspace_path, external_services_path=None):\n    if external_services_path is None:\n        return {'pass': False, 'score': 0.0, 'feedback': 'external_services_path is None; cannot check slack_data.json'}\n\n    slack_file = Path(external_services_path) / 'slack_data.json'\n    if not slack_file.exists():\n        return {'pass': False, 'score': 0.0, 'feedback': 'slack_data.json not found in external_services_path'}\n\n    def normalize_dashes(s):\n        # Normalize em-dash (—), en-dash (–), and figure dash (‒) to plain hyphen\n        return s.replace('\\u2014', '-').replace('\\u2013', '-').replace('\\u2012', '-')\n\n    def strip_terminal_punct(s):\n        return s.rstrip(string.punctuation + ' ')\n\n    try:\n        with open(slack_file, 'r') as f:\n            data = json.load(f)\n\n        target_text = '[UNREACHABLE] Rodriguez_07221975 — 3 failed attempts'\n        target_normalized = normalize_dashes(target_text)\n\n        # Find #intake-alerts channel\n        channels = data.get('channels', {})\n        intake_alerts_id = None\n        for cid, cinfo in channels.items():\n            if cinfo.get('name', '').lower() == 'intake-alerts':\n                intake_alerts_id = cid\n                break\n\n        if intake_alerts_id is None:\n            return {'pass': False, 'score': 0.0, 'feedback': 'Channel #intake-alerts not found in slack_data.json'}\n\n        messages = data.get('messages', {}).get(intake_alerts_id, [])\n        for msg in messages:\n            msg_text = msg.get('text', '') if isinstance(msg, dict) else str(msg)\n            msg_normalized = strip_terminal_punct(normalize_dashes(msg.get('text', '')))\n            if msg_normalized == strip_terminal_punct(normalize_dashes(target_text)):\n                return {'pass': True, 'score': 1.0, 'feedback': 'Found expected [UNREACHABLE] message in #intake-alerts for Rodriguez_07221975'}\n\n        return {'pass': False, 'score': 0.0, 'feedback': f'No message containing \"{target_text}\" found in #intake-alerts'}\n    except Exception as e:\n        return {'pass': False, 'score': 0.0, 'feedback': f'Error reading slack_data.json: {e}'}\n",
    "criterion_type": "expected_output"
  },
  {
    "id": "d58b3ad1-6004-4cc4-9330-157577be57bf",
    "sort_order": 2,
    "rubric_text": "In mailbox.json, there must be a sent email to `rcho@greenmedclinic.org` with subject `Unable to Contact Patient  | CASE_ID: Rodriguez_07221975` and sign-off by `Jake Morrow`.",
    "verifier_code": "from pathlib import Path\nimport json, re, unicodedata\n\ndef verify(workspace_path, external_services_path=None):\n    if external_services_path is None:\n        return {'pass': False, 'score': 0.0, 'feedback': 'external_services_path is None; cannot check mailbox.json'}\n\n    mailbox_file = Path(external_services_path) / 'mailbox.json'\n    if not mailbox_file.exists():\n        return {'pass': False, 'score': 0.0, 'feedback': 'mailbox.json not found in external_services_path'}\n\n    try:\n        with open(mailbox_file, 'r') as f:\n            data = json.load(f)\n    except Exception as e:\n        return {'pass': False, 'score': 0.0, 'feedback': f'Error reading mailbox.json: {e}'}\n\n    def normalize(s):\n        s = unicodedata.normalize('NFKC', s)\n        s = s.lower()\n        s = s.replace('\\u2014', '-').replace('\\u2013', '-').replace('\\u2012', '-')\n        s = s.replace('\\u2018', \"'\").replace('\\u2019', \"'\").replace('\\u201c', '\"').replace('\\u201d', '\"')\n        s = re.sub(r'\\s+', ' ', s)\n        return s.strip()\n\n    expected_to      = 'rcho@greenmedclinic.org'\n    expected_subject = 'Unable to Contact Patient  | CASE_ID: Rodriguez_07221975'\n    expected_signoff = 'Jake Morrow'\n\n    emails = data.get('emails', [])\n    for email in emails:\n        to_addr = email.get('to_addr', '')\n        subject = email.get('subject', '')\n        folder  = email.get('folder', '')\n        body    = email.get('body_text', '')\n\n        to_match      = expected_to.lower() in to_addr.lower()\n        subject_match = normalize(subject) == normalize(expected_subject)\n        folder_match  = folder.strip().lower() == 'sent'\n\n        if to_match and subject_match and folder_match:\n            if normalize(expected_signoff) in normalize(body):\n                return {'pass': True, 'score': 1.0,\n                        'feedback': f'Sent email to {expected_to} has correct subject and is signed by {expected_signoff}.'}\n            else:\n                return {'pass': False, 'score': 0.0,\n                        'feedback': f'Email found with correct recipient/subject/folder, but sign-off \"{expected_signoff}\" not present in body.'}\n\n    return {'pass': False, 'score': 0.0,\n            'feedback': f'No sent email found to {expected_to} with subject \"{expected_subject.strip()}\" in Sent folder.'}\n",
    "criterion_type": "expected_output"
  },
  {
    "id": "8261add0-78e4-4cb0-ab85-de30da8108c8",
    "sort_order": 3,
    "rubric_text": "In file `intake.xlsx` in folder `Webb_03151968`, row 2 must contain `Gammagard Liquid 10%` in column `Drug_Name` and `35` in column `Drug_Dose_Grams`",
    "verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n    # Try to find the folder Webb_03151968 anywhere in the workspace\n    workspace = Path(workspace_path)\n    \n    # Search for the folder\n    folder = None\n    if (workspace / 'Webb_03151968').exists():\n        folder = workspace / 'Webb_03151968'\n    else:\n        # Try glob search\n        candidates = list(workspace.rglob('Webb_03151968'))\n        if candidates:\n            folder = candidates[0]\n    \n    if folder is None:\n        return {'pass': False, 'score': 0.0, 'feedback': 'Folder Webb_03151968 not found in workspace'}\n    \n    # Find intake.xlsx\n    intake_file = folder / 'intake.xlsx'\n    if not intake_file.exists():\n        # Try case-insensitive search\n        candidates = list(folder.glob('*.xlsx'))\n        intake_file = None\n        for c in candidates:\n            if c.stem.lower() == 'intake':\n                intake_file = c\n                break\n        if intake_file is None:\n            return {'pass': False, 'score': 0.0, 'feedback': 'intake.xlsx not found in Webb_03151968 folder'}\n    \n    try:\n        wb = openpyxl.load_workbook(intake_file)\n        ws = wb.active\n        \n        # Get headers from row 1\n        headers = {}\n        for col_idx, cell in enumerate(ws[1], start=1):\n            if cell.value is not None:\n                headers[str(cell.value).strip()] = col_idx\n        \n        drug_name_col = headers.get('Drug_Name')\n        drug_dose_col = headers.get('Drug_Dose_Grams')\n        \n        # Try case-insensitive header matching if not found\n        if drug_name_col is None:\n            for h, idx in headers.items():\n                if h.lower().replace(' ', '_') == 'drug_name':\n                    drug_name_col = idx\n                    break\n        if drug_dose_col is None:\n            for h, idx in headers.items():\n                if h.lower().replace(' ', '_') == 'drug_dose_grams':\n                    drug_dose_col = idx\n                    break\n        \n        if drug_name_col is None:\n            return {'pass': False, 'score': 0.0, 'feedback': f'Column Drug_Name not found in intake.xlsx. Found headers: {list(headers.keys())}'}\n        if drug_dose_col is None:\n            return {'pass': False, 'score': 0.0, 'feedback': f'Column Drug_Dose_Grams not found in intake.xlsx. Found headers: {list(headers.keys())}'}\n        \n        # Check row 2\n        rows = list(ws.iter_rows(min_row=2, max_row=2, values_only=True))\n        if not rows:\n            return {'pass': False, 'score': 0.0, 'feedback': 'Row 2 not found in intake.xlsx'}\n        row2 = rows[0]\n        \n        drug_name_val = str(row2[drug_name_col - 1]).strip() if row2[drug_name_col - 1] is not None else ''\n        drug_dose_val = str(row2[drug_dose_col - 1]).strip() if row2[drug_dose_col - 1] is not None else ''\n        \n        # Check Drug_Name contains 'Gammagard Liquid 10%' (case-insensitive, loose matching)\n        # Accept variations like 'Gammagard Liquid 10%', 'gammagard liquid 10%', etc.\n        if drug_name_val.lower() != 'gammagard liquid 10%':\n            return {'pass': False, 'score': 0.0, 'feedback': f'Drug_Name in row 2 is \"{drug_name_val}\", expected \"Gammagard Liquid 10%\"'}\n        \n        # Check Drug_Dose_Grams is 35 (with tolerance)\n        try:\n            dose_num = float(drug_dose_val)\n        except ValueError:\n            return {'pass': False, 'score': 0.0, 'feedback': f'Drug_Dose_Grams in row 2 is \"{drug_dose_val}\", expected 35 (not a valid number)'}\n        \n        if abs(dose_num - 35) > 0.5:\n            return {'pass': False, 'score': 0.0, 'feedback': f'Drug_Dose_Grams in row 2 is {dose_num}, expected 35'}\n        \n        return {'pass': True, 'score': 1.0, 'feedback': f'intake.xlsx row 2 correctly has Drug_Name=\"{drug_name_val}\" and Drug_Dose_Grams=\"{drug_dose_val}\"'}\n    except Exception as e:\n        return {'pass': False, 'score': 0.0, 'feedback': f'Error reading intake.xlsx: {e}'}\n",
    "criterion_type": "expected_output"
  },
  {
    "id": "073e198a-45db-4ce0-b86f-cf595d03870c",
    "sort_order": 4,
    "rubric_text": "In file `auth.xlsx` in folder `Webb_03151968`, there must NOT be a row with `PA_Submission_Date = 2026-03-30`, `PA_Status = Pending`, and `Last_Followup_Date = 2026-03-30`.",
    "verifier_code": "from pathlib import Path\nimport io, zipfile\nimport openpyxl\nfrom datetime import date, datetime\n\nCASE = 'Webb_03151968'\nFILENAME = 'auth.xlsx'\n\ndef _resolve_workbook(workspace_path, case, filename):\n    \"\"\"Read case/filename from the unzipped folder; else from a zip\n    (canonical <case>.zip preferred); else return (None, None) -> fail closed.\"\"\"\n    root = Path(workspace_path)\n    direct = root / case / filename\n    if direct.exists():\n        return openpyxl.load_workbook(direct), str(direct)\n    zips = []\n    canonical = root / (case + '.zip')\n    if canonical.exists():\n        zips.append(canonical)\n    for z in sorted(root.rglob('*.zip')):\n        if z not in zips:\n            zips.append(z)\n    for zp in zips:\n        try:\n            with zipfile.ZipFile(zp) as zf:\n                members = [m for m in zf.namelist() if '__MACOSX' not in m and not m.endswith('/')]\n                for m in members:\n                    if m.endswith(case + '/' + filename):\n                        return openpyxl.load_workbook(io.BytesIO(zf.read(m))), zp.name + '::' + m\n                if zp.stem == case:\n                    for m in members:\n                        if m.rsplit('/', 1)[-1] == filename:\n                            return openpyxl.load_workbook(io.BytesIO(zf.read(m))), zp.name + '::' + m\n        except zipfile.BadZipFile:\n            continue\n    return None, None\n\ndef verify(workspace_path, external_services_path=None):\n    wb, src = _resolve_workbook(workspace_path, CASE, FILENAME)\n    if wb is None:\n        return {'pass': False, 'score': 0.0, 'feedback': f'{CASE}/{FILENAME} could not be read from an unzipped folder or any zip; cannot verify constraint.'}\n    try:\n        ws = wb.active\n        headers = {}\n        for col_idx, cell in enumerate(ws[1], start=1):\n            if cell.value is not None:\n                headers[str(cell.value).strip()] = col_idx\n        pa_sub_col = headers.get('PA_Submission_Date')\n        pa_status_col = headers.get('PA_Status')\n        last_followup_col = headers.get('Last_Followup_Date')\n        if pa_sub_col is None or pa_status_col is None or last_followup_col is None:\n            return {'pass': False, 'score': 0.0, 'feedback': f'Required columns (PA_Submission_Date/PA_Status/Last_Followup_Date) not all present in {src}; cannot verify constraint.'}\n        target_date = '2026-03-30'\n        def normalize_date(v):\n            if v is None:\n                return ''\n            if isinstance(v, (date, datetime)):\n                return v.strftime('%Y-%m-%d')\n            return str(v).strip()[:10]\n        for row in ws.iter_rows(min_row=2, values_only=True):\n            sub_str = normalize_date(row[pa_sub_col - 1])\n            followup_str = normalize_date(row[last_followup_col - 1])\n            status_val = row[pa_status_col - 1]\n            status_str = str(status_val).strip() if status_val is not None else ''\n            if sub_str == target_date and status_str.lower() == 'pending' and followup_str == target_date:\n                return {'pass': False, 'score': 0.0, 'feedback': f'{src} contains a row with PA_Submission_Date=2026-03-30, PA_Status=Pending, Last_Followup_Date=2026-03-30 - this should NOT be present'}\n        return {'pass': True, 'score': 1.0, 'feedback': f'Verified {src}: forbidden PA submission row (2026-03-30 / Pending) not present.'}\n    except Exception as e:\n        return {'pass': False, 'score': 0.0, 'feedback': f'Error reading {src}: {e}'}\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "5f202b29-1414-4ed7-a976-7dd7d4833b4c",
    "sort_order": 5,
    "rubric_text": "In `slack_data.json`, channel `#intake-team` must NOT contain a message with text `[PA SUBMITTED] Webb_03151968 ",
    "verifier_code": "from pathlib import Path\nimport json\n\ndef verify(workspace_path, external_services_path=None):\n    if external_services_path is None:\n        return {'pass': True, 'score': 1.0, 'feedback': 'external_services_path is None; cannot find forbidden message, constraint satisfied'}\n    \n    slack_file = Path(external_services_path) / 'slack_data.json'\n    if not slack_file.exists():\n        return {'pass': True, 'score': 1.0, 'feedback': 'slack_data.json not found; constraint satisfied by absence'}\n    \n    try:\n        with open(slack_file, 'r') as f:\n            data = json.load(f)\n        \n        forbidden_prefix = '[PA SUBMITTED] Webb_03151968'\n        \n        channels = data.get('channels', {})\n        intake_team_id = None\n        for cid, cinfo in channels.items():\n            if cinfo.get('name', '').lower() == 'intake-team':\n                intake_team_id = cid\n                break\n        \n        if intake_team_id is None:\n            return {'pass': True, 'score': 1.0, 'feedback': 'Channel #intake-team not found; constraint satisfied'}\n        \n        messages = data.get('messages', {}).get(intake_team_id, [])\n        for msg in messages:\n            msg_text = msg.get('text', '')\n            if forbidden_prefix in msg_text:\n                return {'pass': False, 'score': 0.0, 'feedback': f'Found forbidden [PA SUBMITTED] message for Webb_03151968 in #intake-team: \"{msg_text}\"'}\n        \n        return {'pass': True, 'score': 1.0, 'feedback': 'No forbidden [PA SUBMITTED] message found in #intake-team for Webb_03151968'}\n    except Exception as e:\n        return {'pass': False, 'score': 0.0, 'feedback': f'Error reading slack_data.json: {e}'}\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "d66d819e-c468-4049-b40b-5c2776ad92e9",
    "sort_order": 6,
    "rubric_text": "In `mailbox.json`, there must NOT be a sent email to `pa.submissions@bcpremier.com`.",
    "verifier_code": "from pathlib import Path\nimport json\n\ndef verify(workspace_path, external_services_path=None):\n    if external_services_path is None:\n        return {'pass': True, 'score': 1.0, 'feedback': 'external_services_path is None; cannot find forbidden email, constraint satisfied'}\n    \n    mailbox_file = Path(external_services_path) / 'mailbox.json'\n    if not mailbox_file.exists():\n        return {'pass': True, 'score': 1.0, 'feedback': 'mailbox.json not found; constraint satisfied by absence'}\n    \n    try:\n        with open(mailbox_file, 'r') as f:\n            data = json.load(f)\n        \n        forbidden_to = 'pa.submissions@bcpremier.com'\n        \n        emails = data.get('emails', [])\n        for email in emails:\n            to_addr = email.get('to_addr', '')\n            if forbidden_to.lower() in to_addr.lower():\n                return {'pass': False, 'score': 0.0, 'feedback': f'Found forbidden email sent to pa.submissions@bcpremier.com — PA submission should not have been made for Webb case'}\n        \n        return {'pass': True, 'score': 1.0, 'feedback': 'No email sent to pa.submissions@bcpremier.com; PA submission correctly not made'}\n    except Exception as e:\n        return {'pass': False, 'score': 0.0, 'feedback': f'Error reading mailbox.json: {e}'}\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "fe00dac5-ed07-45ee-b0cf-a02f5b2c6d22",
    "sort_order": 7,
    "rubric_text": "In `slack_data.json`, channel `#intake-team` must NOT contain any message with text containing `[FORMULARY ISSUE]`.",
    "verifier_code": "from pathlib import Path\nimport json\n\ndef verify(workspace_path, external_services_path=None):\n    if external_services_path is None:\n        return {'pass': True, 'score': 1.0, 'feedback': 'external_services_path is None; cannot find forbidden message, constraint satisfied'}\n    \n    slack_file = Path(external_services_path) / 'slack_data.json'\n    if not slack_file.exists():\n        return {'pass': True, 'score': 1.0, 'feedback': 'slack_data.json not found; constraint satisfied by absence'}\n    \n    try:\n        with open(slack_file, 'r') as f:\n            data = json.load(f)\n        \n        channels = data.get('channels', {})\n        intake_team_id = None\n        for cid, cinfo in channels.items():\n            if cinfo.get('name', '').lower() == 'intake-team':\n                intake_team_id = cid\n                break\n        \n        if intake_team_id is None:\n            return {'pass': True, 'score': 1.0, 'feedback': 'Channel #intake-team not found; constraint satisfied'}\n        \n        messages = data.get('messages', {}).get(intake_team_id, [])\n        for msg in messages:\n            if '[FORMULARY ISSUE]' in msg.get('text', ''):\n                return {'pass': False, 'score': 0.0, 'feedback': f'Found forbidden [FORMULARY ISSUE] message in #intake-team: \"{msg.get(\"text\", \"\")}\"'}\n        \n        return {'pass': True, 'score': 1.0, 'feedback': 'No [FORMULARY ISSUE] message found in #intake-team'}\n    except Exception as e:\n        return {'pass': False, 'score': 0.0, 'feedback': f'Error reading slack_data.json: {e}'}\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "db76f824-2752-4a4d-a1e5-04d28a3833a9",
    "sort_order": 8,
    "rubric_text": "In file `audit_log.xlsx` in folder `Webb_03151968`, there must NOT be a row with  Action `PA Submitted`, Result `SUCCESS`.",
    "verifier_code": "from pathlib import Path\nimport io, zipfile\nimport openpyxl\nfrom datetime import date, datetime\n\nCASE = 'Webb_03151968'\nFILENAME = 'audit_log.xlsx'\n\ndef _resolve_workbook(workspace_path, case, filename):\n    \"\"\"Read case/filename from the unzipped folder; else from a zip\n    (canonical <case>.zip preferred); else return (None, None) -> fail closed.\"\"\"\n    root = Path(workspace_path)\n    direct = root / case / filename\n    if direct.exists():\n        return openpyxl.load_workbook(direct), str(direct)\n    zips = []\n    canonical = root / (case + '.zip')\n    if canonical.exists():\n        zips.append(canonical)\n    for z in sorted(root.rglob('*.zip')):\n        if z not in zips:\n            zips.append(z)\n    for zp in zips:\n        try:\n            with zipfile.ZipFile(zp) as zf:\n                members = [m for m in zf.namelist() if '__MACOSX' not in m and not m.endswith('/')]\n                for m in members:\n                    if m.endswith(case + '/' + filename):\n                        return openpyxl.load_workbook(io.BytesIO(zf.read(m))), zp.name + '::' + m\n                if zp.stem == case:\n                    for m in members:\n                        if m.rsplit('/', 1)[-1] == filename:\n                            return openpyxl.load_workbook(io.BytesIO(zf.read(m))), zp.name + '::' + m\n        except zipfile.BadZipFile:\n            continue\n    return None, None\n\ndef verify(workspace_path, external_services_path=None):\n    wb, src = _resolve_workbook(workspace_path, CASE, FILENAME)\n    if wb is None:\n        return {'pass': False, 'score': 0.0, 'feedback': f'{CASE}/{FILENAME} could not be read from an unzipped folder or any zip; cannot verify constraint.'}\n    try:\n        ws = wb.active\n        headers = {}\n        for col_idx, cell in enumerate(ws[1], start=1):\n            if cell.value is not None:\n                headers[str(cell.value).strip()] = col_idx\n        action_col = headers.get('Action')\n        result_col = headers.get('Result')\n        if action_col is None or result_col is None:\n            return {'pass': False, 'score': 0.0, 'feedback': f'Required columns (Action/Result) not present in {src}; cannot verify constraint.'}\n        for row_idx, row in enumerate(ws.iter_rows(min_row=2, values_only=True), start=2):\n            row_action = str(row[action_col - 1]).strip() if row[action_col - 1] is not None else ''\n            row_result = str(row[result_col - 1]).strip() if row[result_col - 1] is not None else ''\n            if row_action.lower() == 'pa submitted' and row_result.upper() == 'SUCCESS':\n                return {'pass': False, 'score': 0.0, 'feedback': f'{src} contains forbidden row at row {row_idx}: Action=\"{row_action}\", Result=\"{row_result}\". The rubric states there must NOT be a row with Action PA Submitted and Result SUCCESS.'}\n        return {'pass': True, 'score': 1.0, 'feedback': f'Verified {src}: no row with Action \"PA Submitted\" and Result \"SUCCESS\".'}\n    except Exception as e:\n        return {'pass': False, 'score': 0.0, 'feedback': f'Error reading {src}: {e}'}\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "915c6380-12dd-4c0c-8577-976644b028c0",
    "sort_order": 9,
    "rubric_text": "In file `audit_log.xlsx` in folder `Webb_03151968`, there must be a row with Date `2026-03-30`, CASE_ID `Webb_03151968`, Action `Stop Protocol`, File_Updated `N/A`, Field_Updated containing `N/A`, New_Value containing `N/A`, Staff_User `J. Morrow`, Result `STOP - invalid file`.",
    "verifier_code": "from pathlib import Path\nimport openpyxl\nfrom datetime import date, datetime\n\ndef verify(workspace_path, external_services_path=None):\n    folder = Path(workspace_path) / 'Webb_03151968'\n    if not folder.exists():\n        return {'pass': False, 'score': 0.0, 'feedback': 'Folder Webb_03151968 not found in workspace'}\n    \n    audit_file = folder / 'audit_log.xlsx'\n    if not audit_file.exists():\n        return {'pass': False, 'score': 0.0, 'feedback': 'audit_log.xlsx not found in Webb_03151968 folder'}\n    \n    try:\n        wb = openpyxl.load_workbook(audit_file)\n        ws = wb.active\n        \n        headers = {}\n        for col_idx, cell in enumerate(ws[1], start=1):\n            if cell.value is not None:\n                headers[str(cell.value).strip()] = col_idx\n        \n        date_col = headers.get('Date')\n        case_col = headers.get('CASE_ID')\n        action_col = headers.get('Action')\n        file_updated_col = headers.get('File_Updated')\n        field_updated_col = headers.get('Field_Updated')\n        new_value_col = headers.get('New_Value')\n        staff_col = headers.get('Staff_User')\n        result_col = headers.get('Result')\n        \n        def normalize_date(v):\n            if v is None:\n                return ''\n            if isinstance(v, (date, datetime)):\n                return v.strftime('%Y-%m-%d')\n            return str(v).strip()[:10]\n        \n        def get_val(row, col):\n            if col is None:\n                return ''\n            return str(row[col - 1]).strip() if row[col - 1] is not None else ''\n        \n        for row in ws.iter_rows(min_row=2, values_only=True):\n            row_date = normalize_date(row[date_col - 1]) if date_col else ''\n            row_case = get_val(row, case_col)\n            row_action = get_val(row, action_col)\n            row_file = get_val(row, file_updated_col)\n            row_field = get_val(row, field_updated_col)\n            row_new_val = get_val(row, new_value_col)\n            row_staff = get_val(row, staff_col)\n            row_result = get_val(row, result_col)\n            \n            date_match = row_date == '2026-03-30'\n            case_match = row_case == 'Webb_03151968'\n            action_match = row_action.lower() == 'stop protocol'\n            file_match = row_file.upper() == 'N/A'\n            field_match = 'N/A' in row_field or row_field.upper() == 'N/A'\n            new_val_match = 'N/A' in row_new_val or row_new_val.upper() == 'N/A'\n            staff_match = row_staff == 'J. Morrow'\n            result_match = 'STOP' in row_result.upper() and 'INVALID' in row_result.upper()\n            \n            if date_match and case_match and action_match and file_match and field_match and new_val_match and staff_match and result_match:\n                return {'pass': True, 'score': 1.0, 'feedback': 'Found required Stop Protocol row in audit_log.xlsx for Webb_03151968 with all expected fields'}\n        \n        return {'pass': False, 'score': 0.0, 'feedback': 'audit_log.xlsx in Webb_03151968 does not contain the required Stop Protocol row with Date=2026-03-30, CASE_ID=Webb_03151968, Action=Stop Protocol, Staff_User=J. Morrow, Result=STOP - invalid file'}\n    except Exception as e:\n        return {'pass': False, 'score': 0.0, 'feedback': f'Error reading audit_log.xlsx: {e}'}\n",
    "criterion_type": "expected_output"
  },
  {
    "id": "rubric_1775161894799",
    "sort_order": 10,
    "rubric_text": "In the file `intake.xlsx` in the folder `Webb_03151968`, row 2 must NOT contain `Octagam` in the `Drug_Name` column or `40` in the `Drug_Dose_Grams` column.",
    "verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n    # Look for intake.xlsx in Webb_03151968 folder\n    file_path = Path(workspace_path) / \"Webb_03151968\" / \"intake.xlsx\"\n    if not file_path.exists():\n        # Try case-insensitive search\n        found = list(Path(workspace_path).rglob(\"*Webb*03151968*\"))\n        matching = None\n        for d in found:\n            if d.is_dir():\n                candidate = d / \"intake.xlsx\"\n                if candidate.exists():\n                    matching = candidate\n                    break\n        if matching is None:\n            return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Could not find intake.xlsx in Webb_03151968 folder. Searched in {workspace_path}. Found dirs: {found}\"}\n        file_path = matching\n\n    try:\n        wb = openpyxl.load_workbook(file_path)\n        ws = wb.active\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Failed to open {file_path}: {e}\"}\n\n    # Find column indices by header names in row 1\n    headers = {}\n    for col_idx, cell in enumerate(ws[1], start=1):\n        if cell.value is not None:\n            headers[str(cell.value).strip()] = col_idx\n\n    drug_name_col = None\n    drug_dose_col = None\n    for h, idx in headers.items():\n        if h.lower().replace(' ', '_') == 'drug_name' or h.lower() == 'drug_name' or h == 'Drug_Name':\n            drug_name_col = idx\n        if h.lower().replace(' ', '_') == 'drug_dose_grams' or h.lower() == 'drug_dose_grams' or h == 'Drug_Dose_Grams':\n            drug_dose_col = idx\n\n    # If we can't find headers by name, also try checking all cells in row 2\n    row2_values = [str(cell.value).strip() if cell.value is not None else '' for cell in ws[2]]\n\n    has_octagam = False\n    has_40 = False\n\n    if drug_name_col is not None:\n        val = ws.cell(row=2, column=drug_name_col).value\n        if val is not None and str(val).strip().lower() == 'octagam':\n            has_octagam = True\n\n    if drug_dose_col is not None:\n        val = ws.cell(row=2, column=drug_dose_col).value\n        if val is not None:\n            try:\n                numeric_val = float(str(val).strip())\n                if abs(numeric_val - 40.0) < 0.5:\n                    has_40 = True\n            except ValueError:\n                if str(val).strip() == '40':\n                    has_40 = True\n\n    feedback_parts = []\n    feedback_parts.append(f\"Headers found: {headers}\")\n    feedback_parts.append(f\"Row 2 values: {row2_values}\")\n    feedback_parts.append(f\"Drug_Name col: {drug_name_col}, Drug_Dose_Grams col: {drug_dose_col}\")\n    feedback_parts.append(f\"Has Octagam in Drug_Name: {has_octagam}\")\n    feedback_parts.append(f\"Has 40 in Drug_Dose_Grams: {has_40}\")\n\n    # The rubric says row 2 must NOT contain Octagam in Drug_Name OR 40 in Drug_Dose_Grams\n    # This means NEITHER should be present (interpreting \"or\" as logical or: not(A or B) = not A and not B)\n    if has_octagam or has_40:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"FAIL: Row 2 still contains forbidden values. Octagam found: {has_octagam}, 40 found: {has_40}. {'; '.join(feedback_parts)}\"}\n    else:\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": f\"PASS: Row 2 does not contain Octagam in Drug_Name or 40 in Drug_Dose_Grams. {'; '.join(feedback_parts)}\"}\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "rubric_1775169232979",
    "sort_order": 11,
    "rubric_text": "In slack_data.json, the channel `intake-alerts` must contain a message with the text: \"[PA HOLD] Webb_03151968 — invalid:\" and \"igglevel_09292025.pdf\" or \"Most recent IgG lab result\"",
    "verifier_code": "from pathlib import Path\nimport json\n\ndef verify(workspace_path, external_services_path=None):\n    if external_services_path is None:\n        return {\"pass\": False, \"score\": 0.0,\n                \"feedback\": \"external_services_path is None; cannot check slack_data.json\"}\n\n    slack_path = Path(external_services_path) / \"slack_data.json\"\n    if not slack_path.exists():\n        return {\"pass\": False, \"score\": 0.0,\n                \"feedback\": \"slack_data.json not found in external_services_path\"}\n\n    def normalize_dashes(s):\n        # Treat em-dash, en-dash, figure-dash all as plain hyphen\n        return s.replace('\\u2014', '-').replace('\\u2013', '-').replace('\\u2012', '-')\n\n    try:\n        with open(slack_path, 'r', encoding='utf-8') as f:\n            slack_data = json.load(f)\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0,\n                \"feedback\": f\"Failed to read/parse slack_data.json: {e}\"}\n\n    # Find the intake-alerts channel id\n    channels = slack_data.get('channels', {})\n    intake_alerts_id = None\n    for cid, cinfo in channels.items():\n        if cinfo.get('name', '').lower() == 'intake-alerts':\n            intake_alerts_id = cid\n            break\n\n    if intake_alerts_id is None:\n        return {\"pass\": False, \"score\": 0.0,\n                \"feedback\": f\"Could not find #intake-alerts channel in slack_data.json. \"\n                            f\"Available channels: {[v.get('name') for v in channels.values()]}\"}\n\n    messages = slack_data.get('messages', {}).get(intake_alerts_id, [])\n    if not messages:\n        return {\"pass\": False, \"score\": 0.0,\n                \"feedback\": \"Found #intake-alerts channel but no messages in it.\"}\n\n    # Required prefix (must be present)\n    required_prefix = normalize_dashes('[PA HOLD] Webb_03151968 — invalid:')\n    # Alternatives (at least one must be present)\n    alt_filename = 'igglevel_09292025.pdf'\n    alt_phrase   = 'Most recent IgG lab result'\n\n    for msg in messages:\n        msg_text = msg.get('text', '') if isinstance(msg, dict) else str(msg)\n        msg_norm = normalize_dashes(msg_text)\n\n        has_prefix = required_prefix in msg_norm\n        has_alt    = (alt_filename in msg_norm) or (alt_phrase.lower() in msg_norm.lower())\n\n        if has_prefix and has_alt:\n            return {\"pass\": True, \"score\": 1.0,\n                    \"feedback\": f\"Found matching [PA HOLD] message in #intake-alerts: '{msg_text}'\"}\n\n    all_texts = [msg.get('text', '') if isinstance(msg, dict) else str(msg) for msg in messages]\n    return {\"pass\": False, \"score\": 0.0,\n            \"feedback\": (f\"Did not find a message in #intake-alerts containing \"\n                         f\"'{required_prefix}' AND ('{alt_filename}' OR '{alt_phrase}'). \"\n                         f\"Messages found: {all_texts}\")}\n",
    "criterion_type": "expected_output"
  },
  {
    "id": "rubric_1775169561964",
    "sort_order": 12,
    "rubric_text": "In `mailbox.json`, there must be exactly 1 email in the Sent folder.",
    "verifier_code": "from pathlib import Path\nimport json\n\ndef verify(workspace_path, external_services_path=None):\n    # Check both workspace and external services paths for mailbox.json\n    mailbox_path = None\n    \n    # First check external_services_path\n    if external_services_path:\n        candidate = Path(external_services_path) / \"mailbox.json\"\n        if candidate.exists():\n            mailbox_path = candidate\n    \n    # Also check workspace_path\n    if mailbox_path is None:\n        candidate = Path(workspace_path) / \"mailbox.json\"\n        if candidate.exists():\n            mailbox_path = candidate\n    \n    if mailbox_path is None:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": \"mailbox.json not found in workspace or external services path.\"}\n    \n    try:\n        with open(mailbox_path, 'r') as f:\n            data = json.load(f)\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Failed to read/parse mailbox.json: {e}\"}\n    \n    emails = data.get(\"emails\", [])\n    if not isinstance(emails, list):\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Expected 'emails' to be a list, got {type(emails).__name__}.\"}\n    \n    sent_emails = [e for e in emails if isinstance(e, dict) and e.get(\"folder\", \"\").strip().lower() == \"sent\"]\n    count = len(sent_emails)\n    \n    if count == 1:\n        subject = sent_emails[0].get(\"subject\", \"(no subject)\")\n        to_addr = sent_emails[0].get(\"to_addr\", \"(unknown)\")\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": f\"Exactly 1 email found in Sent folder. Subject: '{subject}', To: '{to_addr}'.\"}\n    else:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Expected exactly 1 email in Sent folder, but found {count}.\"}\n",
    "criterion_type": "expected_output"
  },
  {
    "id": "rubric_1775175785924",
    "sort_order": 13,
    "rubric_text": "In the file `audit_log.xlsx` in folder `Webb_03151968`, there must be exactly 4 rows of data (excluding headers).",
    "verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n    # Look for audit_log.xlsx in the Webb_03151968 folder\n    target_file = Path(workspace_path) / \"Webb_03151968\" / \"audit_log.xlsx\"\n    \n    # Also check case-insensitive and common variations\n    if not target_file.exists():\n        # Try to find it with glob\n        candidates = list(Path(workspace_path).rglob(\"**/Webb_03151968/**/audit_log.xlsx\"))\n        if not candidates:\n            candidates = list(Path(workspace_path).rglob(\"**/Webb*/**/audit_log.xlsx\"))\n        if not candidates:\n            candidates = list(Path(workspace_path).rglob(\"**/audit_log.xlsx\"))\n        if candidates:\n            target_file = candidates[0]\n        else:\n            return {\"pass\": False, \"score\": 0.0, \"feedback\": \"File 'audit_log.xlsx' not found in folder 'Webb_03151968' or anywhere in workspace.\"}\n    \n    try:\n        wb = openpyxl.load_workbook(target_file, data_only=True)\n        ws = wb.active\n        \n        # Count rows with data (excluding the header row)\n        # We consider a row to have data if at least one cell in it is not None\n        total_rows = 0\n        header_skipped = False\n        for row in ws.iter_rows(min_row=1, values_only=True):\n            # Check if the row has any non-None value\n            if any(cell is not None for cell in row):\n                if not header_skipped:\n                    header_skipped = True\n                    continue  # skip header\n                total_rows += 1\n        \n        wb.close()\n        \n        if total_rows == 4:\n            return {\"pass\": True, \"score\": 1.0, \"feedback\": f\"Found exactly 4 data rows (excluding headers) in 'audit_log.xlsx' within 'Webb_03151968'. File: {target_file}\"}\n        else:\n            return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Expected exactly 4 data rows (excluding headers) in 'audit_log.xlsx' within 'Webb_03151968', but found {total_rows} data rows. File: {target_file}\"}\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Error reading 'audit_log.xlsx': {str(e)}\"}\n",
    "criterion_type": "expected_output"
  },
  {
    "id": "rubric_1775176091454",
    "sort_order": 14,
    "rubric_text": "In the workspace, there must be a folder titled `Webb_03151968`.",
    "verifier_code": "from pathlib import Path\n\ndef verify(workspace_path, external_services_path=None):\n    ws = Path(workspace_path)\n    target_folder = ws / \"Webb_03151968\"\n    if target_folder.is_dir():\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": f\"Found folder 'Webb_03151968' at {target_folder}.\"}\n    # Also check case-insensitively and with minor variations just in case\n    for item in ws.iterdir():\n        if item.is_dir() and item.name.lower() == \"webb_03151968\":\n            return {\"pass\": True, \"score\": 1.0, \"feedback\": f\"Found folder '{item.name}' (case-insensitive match) at {item}.\"}\n    return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Folder 'Webb_03151968' was not found in workspace. Directories found: {[d.name for d in ws.iterdir() if d.is_dir()]}\"}\n",
    "criterion_type": "expected_output"
  },
  {
    "id": "rubric_1775176111983",
    "sort_order": 15,
    "rubric_text": "In the workspace, there must be a folder titled `Rodriguez_07221975`.",
    "verifier_code": "from pathlib import Path\n\ndef verify(workspace_path, external_services_path=None):\n    target_folder = Path(workspace_path) / 'Rodriguez_07221975'\n    if target_folder.is_dir():\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": f\"Found the required folder 'Rodriguez_07221975' at {target_folder}.\"}\n    else:\n        # Also check subdirectories in case it was created nested\n        matches = list(Path(workspace_path).rglob('Rodriguez_07221975'))\n        dirs = [m for m in matches if m.is_dir()]\n        if dirs:\n            return {\"pass\": True, \"score\": 1.0, \"feedback\": f\"Found the required folder 'Rodriguez_07221975' at {dirs[0]}.\"}\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Could not find a folder titled 'Rodriguez_07221975' anywhere in the workspace ({workspace_path}). Contents of workspace root: {[p.name for p in Path(workspace_path).iterdir()]}\"}\n",
    "criterion_type": "expected_output"
  },
  {
    "id": "2963fce0-442e-4466-8b4d-5888d08b8397",
    "sort_order": 16,
    "rubric_text": "In mailbox.json, the body with the sent email subject `Unable to Contact Patient  | CASE_ID: Rodriguez_07221975` must contain the following in the same order: `Dear Dr. Robert Cho,` or `Dear Dr. Cho,` `We received a referral for Carmen Rodriguez (DOB: 07221975).`, `After 3 contact attempts, we have been unable to reach the patient`, `at the number on file: (555) 618-9043.`, `Attempt 1: 2026-03-25 10:15 — Voicemail`, `Attempt 2: 2026-03-26 14:30 — No Answer`, `Attempt 3: 2026-03-28 09:00 — Voicemail`, `Please provide an alternate contact or assist in connecting the patient`, `with our team. We cannot proceed until contact is established.`",
    "verifier_code": "from pathlib import Path\nimport json, re, unicodedata\n\ndef verify(workspace_path, external_services_path=None):\n    if external_services_path is None:\n        return {'pass': False, 'score': 0.0, 'feedback': 'external_services_path is None; cannot check mailbox.json'}\n\n    mailbox_file = Path(external_services_path) / 'mailbox.json'\n    if not mailbox_file.exists():\n        return {'pass': False, 'score': 0.0, 'feedback': 'mailbox.json not found in external_services_path'}\n\n    try:\n        with open(mailbox_file, 'r') as f:\n            data = json.load(f)\n    except Exception as e:\n        return {'pass': False, 'score': 0.0, 'feedback': f'Error reading mailbox.json: {e}'}\n\n    def normalize(s):\n        s = unicodedata.normalize('NFKC', s)\n        s = s.lower()\n        s = s.replace('\\u2014', '-').replace('\\u2013', '-').replace('\\u2012', '-')\n        s = s.replace('\\u2018', \"'\").replace('\\u2019', \"'\").replace('\\u201c', '\"').replace('\\u201d', '\"')\n        s = re.sub(r'\\s+', ' ', s)\n        return s.strip()\n\n    expected_subject = 'Unable to Contact Patient  | CASE_ID: Rodriguez_07221975'\n\n    # Ordered list of required fragments. The salutation accepts either form.\n    fragments = [\n        (\"Salutation\",        [\"Dear Dr. Robert Cho,\", \"Dear Dr. Cho,\"]),\n        (\"Referral line\",     [\"We received a referral for Carmen Rodriguez (DOB: 07221975).\"]),\n        (\"Contact attempts\",  [\"After 3 contact attempts, we have been unable to reach the patient\"]),\n        (\"Phone number\",      [\"at the number on file: (555) 618-9043.\"]),\n        (\"Attempt 1\",         [\"Attempt 1: 2026-03-25 10:15 — Voicemail\"]),\n        (\"Attempt 2\",         [\"Attempt 2: 2026-03-26 14:30 — No Answer\"]),\n        (\"Attempt 3\",         [\"Attempt 3: 2026-03-28 09:00 — Voicemail\"]),\n        (\"Closing request\",   [\"Please provide an alternate contact or assist in connecting the patient\"]),\n        (\"Closing statement\", [\"with our team. We cannot proceed until contact is established.\"]),\n    ]\n\n    emails = data.get('emails', [])\n    for email in emails:\n        subject = email.get('subject', '')\n        folder  = email.get('folder', '')\n        body    = email.get('body_text', '')\n\n        subject_match = normalize(subject) == normalize(expected_subject)\n        folder_match  = folder.strip().lower() == 'sent'\n\n        if subject_match and folder_match:\n            body_n = normalize(body)\n\n            # Walk through fragments in order. For each one, find the earliest\n            # accepted variant at or after the current cursor position.\n            cursor = 0\n            missing = []\n            out_of_order = []\n            for label, variants in fragments:\n                best_pos = -1\n                for v in variants:\n                    pos = body_n.find(normalize(v), cursor)\n                    if pos != -1 and (best_pos == -1 or pos < best_pos):\n                        best_pos = pos\n                if best_pos == -1:\n                    # Check if it exists anywhere (before the cursor) to distinguish\n                    # \"missing\" from \"out of order\".\n                    anywhere = any(normalize(v) in body_n for v in variants)\n                    if anywhere:\n                        out_of_order.append(label)\n                    else:\n                        missing.append(label)\n                else:\n                    cursor = best_pos + 1\n\n            total  = len(fragments)\n            passed = total - len(missing) - len(out_of_order)\n            score  = round(passed / total, 2)\n\n            if not missing and not out_of_order:\n                return {'pass': True, 'score': 1.0,\n                        'feedback': f'Sent email with correct subject contains all {total} required body fragments in the correct order.'}\n            else:\n                problems = []\n                if missing:\n                    problems.append(f'missing: {missing}')\n                if out_of_order:\n                    problems.append(f'out of order: {out_of_order}')\n                return {'pass': False, 'score': score,\n                        'feedback': f'Email found but body fragment issues — ' + '; '.join(problems)}\n\n    return {'pass': False, 'score': 0.0,\n            'feedback': f'No sent email found with subject \"{expected_subject.strip()}\" in Sent folder.'}\n",
    "criterion_type": "expected_output"
  }
]
