[
  {
    "id": "72b5011c-bafc-42bb-b6bb-200cc6c0df58",
    "sort_order": 0,
    "rubric_text": "file `receiving_log.xlsx`, has 5 rows (2 headers, and 3 data)",
    "verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n    file_path = Path(workspace_path) / \"receiving_log.xlsx\"\n    if not file_path.exists():\n        fallback = [f for f in Path(workspace_path).glob(\"*.xlsx\") if \"receiving\" in f.name.lower()]\n        if not fallback:\n            return {\"pass\": False, \"score\": 0.0, \"feedback\": \"receiving_log.xlsx not found in workspace.\"}\n        file_path = fallback[0]\n\n    try:\n        wb = openpyxl.load_workbook(str(file_path), data_only=True)\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Could not open {file_path.name}: {e}\"}\n\n    ws = wb.active\n    non_empty_rows = [\n        row_idx + 1\n        for row_idx, row in enumerate(ws.iter_rows(values_only=True))\n        if any(cell is not None for cell in row)\n    ]\n    total = len(non_empty_rows)\n\n    if total == 5:\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": f\"{file_path.name} has 5 non-empty rows (2 header + 3 data) as expected.\"}\n    else:\n        return {\n            \"pass\": False,\n            \"score\": 0.0,\n            \"feedback\": (\n                f\"{file_path.name} has {total} non-empty rows, expected exactly 5 (2 header + 3 data). \"\n                f\"Non-empty row numbers: {non_empty_rows}.\"\n            ),\n        }\n",
    "criterion_type": "expected_output"
  },
  {
    "id": "rubric_1776277812496",
    "sort_order": 1,
    "rubric_text": "`receiving_log.xlsx`: Row 3 must contain the following values in order: REC-20261124-001 | MER-PO-204869 | 001 | LB-03341-B2 | N | SUP-0147 | XPO Logistics | 11/24/2026 13:12 CST |  | Dock 1 | 12 | 12 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  | JR |  | In Process |  |",
    "verifier_code": "from pathlib import Path\nimport re\nimport openpyxl\nfrom datetime import datetime\n\ndef verify(workspace_path, external_services_path=None):\n    file_path = Path(workspace_path) / \"receiving_log.xlsx\"\n    if not file_path.exists():\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": \"receiving_log.xlsx not found in workspace.\"}\n\n    try:\n        wb = openpyxl.load_workbook(str(file_path), data_only=True)\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Could not open receiving_log.xlsx: {e}\"}\n\n    ws = wb.active\n\n    # Expected values for row 3 (columns A–AE, 31 columns).\n    # None = cell must be empty.\n    expected = [\n        \"REC-20261124-001\",      # A\n        \"MER-PO-204869\",         # B\n        \"001\",                   # C\n        \"LB-03341-B2\",           # D\n        \"N\",                     # E\n        \"SUP-0147\",              # F\n        \"XPO Logistics\",         # G\n        \"11/24/2026 13:12 CST\",  # H\n        None,                    # I\n        \"Dock 1\",                # J\n        12,                      # K\n        12,                      # L\n        None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,  # M–AA\n        \"JR\",                    # AB\n        None,                    # AC\n        \"In Process\",            # AD\n        None,                    # AE\n    ]\n\n    def cell_empty(val):\n        return val is None or str(val).strip() == \"\"\n\n    def normalize(val):\n        if val is None:\n            return \"\"\n        return re.sub(r'\\s+', ' ', str(val).strip())\n\n    def values_match(exp, actual):\n        if exp is None:\n            return cell_empty(actual)\n\n        if cell_empty(actual):\n            return False\n\n        exp_str = normalize(exp)\n        act_str = normalize(actual)\n\n        # Case-insensitive exact match\n        if exp_str.lower() == act_str.lower():\n            return True\n\n        # datetime objects written by openpyxl (date/time columns)\n        if isinstance(actual, datetime):\n            formatted = actual.strftime(\"%m/%d/%Y %H:%M\")\n            exp_core = re.sub(r'\\s*CST\\s*$', '', exp_str, flags=re.IGNORECASE).strip()\n            return exp_core == formatted\n\n        # Numeric match for integer-like values (e.g. \"001\" stored as 1, 12 stored as \"12\")\n        try:\n            return float(exp_str) == float(act_str)\n        except (ValueError, TypeError):\n            pass\n\n        return False\n\n    mismatches = []\n    matched = 0\n\n    for i, exp_val in enumerate(expected):\n        actual_val = ws.cell(row=3, column=i + 1).value\n        col_letter = openpyxl.utils.get_column_letter(i + 1)\n        if values_match(exp_val, actual_val):\n            matched += 1\n        else:\n            exp_display = \"empty\" if exp_val is None else f\"'{exp_val}'\"\n            act_display = \"empty\" if cell_empty(actual_val) else f\"'{normalize(actual_val)}'\"\n            mismatches.append(f\"  Col {col_letter}: expected {exp_display}, found {act_display}\")\n\n    total = len(expected)\n    score = round(matched / total, 4)\n\n    if not mismatches:\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": \"Row 3 matches all 31 expected values.\"}\n\n    return {\n        \"pass\": False,\n        \"score\": score,\n        \"feedback\": f\"Row 3 has {len(mismatches)}/{total} field(s) wrong:\\n\" + \"\\n\".join(mismatches),\n    }\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "rubric_1776277813709",
    "sort_order": 2,
    "rubric_text": "`receiving_log.xlsx`: Row 4 must contain the following values in order: REC-20261124-002 | MER-PO-204870 | 001 | LB-07752-A3 | N | SUP-0147 | XPO Logistics | 11/24/2026 13:24 CST | | Dock 3 | 40 | 40 | | | | | | | | | | | | | | | | JR | | In Process | |",
    "verifier_code": "from pathlib import Path\nimport re\nimport openpyxl\nfrom datetime import datetime\n\ndef verify(workspace_path, external_services_path=None):\n    file_path = Path(workspace_path) / \"receiving_log.xlsx\"\n    if not file_path.exists():\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": \"receiving_log.xlsx not found in workspace.\"}\n\n    try:\n        wb = openpyxl.load_workbook(str(file_path), data_only=True)\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Could not open receiving_log.xlsx: {e}\"}\n\n    ws = wb.active\n\n    # Expected values for row 4 (columns A–AE, 31 columns).\n    # None = cell must be empty.\n    expected = [\n        \"REC-20261124-002\",      # A\n        \"MER-PO-204870\",         # B\n        \"001\",                   # C\n        \"LB-07752-A3\",           # D\n        \"N\",                     # E\n        \"SUP-0147\",              # F\n        \"XPO Logistics\",         # G\n        \"11/24/2026 13:24 CST\",  # H\n        None,                    # I\n        \"Dock 3\",                # J\n        40,                      # K\n        40,                      # L\n        None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,  # M–AA\n        \"JR\",                    # AB\n        None,                    # AC\n        \"In Process\",            # AD\n        None,                    # AE\n    ]\n\n    def cell_empty(val):\n        return val is None or str(val).strip() == \"\"\n\n    def normalize(val):\n        if val is None:\n            return \"\"\n        return re.sub(r'\\s+', ' ', str(val).strip())\n\n    def values_match(exp, actual):\n        if exp is None:\n            return cell_empty(actual)\n\n        if cell_empty(actual):\n            return False\n\n        exp_str = normalize(exp)\n        act_str = normalize(actual)\n\n        if exp_str.lower() == act_str.lower():\n            return True\n\n        if isinstance(actual, datetime):\n            formatted = actual.strftime(\"%m/%d/%Y %H:%M\")\n            exp_core = re.sub(r'\\s*CST\\s*$', '', exp_str, flags=re.IGNORECASE).strip()\n            return exp_core == formatted\n\n        try:\n            return float(exp_str) == float(act_str)\n        except (ValueError, TypeError):\n            pass\n\n        return False\n\n    mismatches = []\n    matched = 0\n\n    for i, exp_val in enumerate(expected):\n        actual_val = ws.cell(row=4, column=i + 1).value\n        col_letter = openpyxl.utils.get_column_letter(i + 1)\n        if values_match(exp_val, actual_val):\n            matched += 1\n        else:\n            exp_display = \"empty\" if exp_val is None else f\"'{exp_val}'\"\n            act_display = \"empty\" if cell_empty(actual_val) else f\"'{normalize(actual_val)}'\"\n            mismatches.append(f\"  Col {col_letter}: expected {exp_display}, found {act_display}\")\n\n    total = len(expected)\n    score = round(matched / total, 4)\n\n    if not mismatches:\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": \"Row 4 matches all 31 expected values.\"}\n\n    return {\n        \"pass\": False,\n        \"score\": score,\n        \"feedback\": f\"Row 4 has {len(mismatches)}/{total} field(s) wrong:\\n\" + \"\\n\".join(mismatches),\n    }\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "rubric_1776277827542",
    "sort_order": 3,
    "rubric_text": "receiving_log.xlsx: Row 5 must contain the following values in order: REC-20261124-003 | MER-PO-204871 | 001 | FL-08814-A1 | N | SUP-0147 | XPO Logistics | 11/24/2026 13:30 CST | 11/24/2026 14:21 CST | Dock 2 | 24 | 24 | 24 | 0 | 0.0% | Exact | Accepted | Tier 0 | Accepted | N/A | Verified | Accepted | | | | ACCEPTED CLEAN | | JR | Actioned per SOP protocol | Posted | |",
    "verifier_code": "from pathlib import Path\nimport re\nimport openpyxl\nfrom datetime import datetime\n\ndef verify(workspace_path, external_services_path=None):\n    file_path = Path(workspace_path) / \"receiving_log.xlsx\"\n    if not file_path.exists():\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": \"receiving_log.xlsx not found in workspace.\"}\n\n    try:\n        wb = openpyxl.load_workbook(str(file_path), data_only=True)\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Could not open receiving_log.xlsx: {e}\"}\n\n    ws = wb.active\n\n    # Expected values for row 5 (columns A–AE, 31 columns).\n    # None = cell must be empty.\n    expected = [\n        \"REC-20261124-003\",           # A\n        \"MER-PO-204871\",              # B\n        \"001\",                        # C\n        \"FL-08814-A1\",                # D\n        \"N\",                          # E\n        \"SUP-0147\",                   # F\n        \"XPO Logistics\",              # G\n        \"11/24/2026 13:30 CST\",       # H\n        \"11/24/2026 14:21 CST\",       # I\n        \"Dock 2\",                     # J\n        24,                           # K\n        24,                           # L\n        24,                           # M\n        0,                            # N\n        \"0.0%\",                       # O\n        \"Exact\",                      # P\n        \"Accepted\",                   # Q\n        \"Tier 0\",                     # R\n        \"Accepted\",                   # S\n        \"N/A\",                        # T\n        \"Verified\",                   # U\n        \"Accepted\",                   # V\n        None,                         # W (Heat_Numbers)\n        None,                         # X (COC_Filename)\n        None,                         # Y (MTR_Filename)\n        \"ACCEPTED CLEAN\",             # Z\n        None,                         # AA (NCR_Number)\n        \"JR\",                         # AB\n        \"Actioned per SOP protocol\",  # AC\n        \"Posted\",                     # AD\n        None,                         # AE (Handoff_Notes)\n    ]\n\n    def cell_empty(val):\n        return val is None or str(val).strip() == \"\"\n\n    def normalize(val):\n        if val is None:\n            return \"\"\n        return re.sub(r'\\s+', ' ', str(val).strip())\n\n    def values_match(exp, actual):\n        if exp is None:\n            return cell_empty(actual)\n\n        if cell_empty(actual):\n            return False\n\n        exp_str = normalize(exp)\n        act_str = normalize(actual)\n\n        # Case-insensitive exact match\n        if exp_str.lower() == act_str.lower():\n            return True\n\n        # Percentage field (col O): \"0.0%\" — accept \"0%\", \"0.00%\", or numeric 0\n        if \"%\" in exp_str:\n            exp_num = float(exp_str.replace(\"%\", \"\").strip())\n            try:\n                act_num = float(act_str.replace(\"%\", \"\").strip())\n                return exp_num == act_num\n            except (ValueError, TypeError):\n                pass\n            try:\n                act_float = float(str(actual))\n                # stored as decimal fraction (0.0) or as percentage number (0.0)\n                return exp_num == act_float or exp_num == act_float * 100\n            except (ValueError, TypeError):\n                pass\n            return False\n\n        # datetime objects written by openpyxl\n        if isinstance(actual, datetime):\n            formatted = actual.strftime(\"%m/%d/%Y %H:%M\")\n            exp_core = re.sub(r'\\s*CST\\s*$', '', exp_str, flags=re.IGNORECASE).strip()\n            return exp_core == formatted\n\n        # Numeric exact match (e.g. \"001\" stored as 1, 24 stored as \"24\")\n        try:\n            return float(exp_str) == float(act_str)\n        except (ValueError, TypeError):\n            pass\n\n        return False\n\n    mismatches = []\n    matched = 0\n\n    for i, exp_val in enumerate(expected):\n        actual_val = ws.cell(row=5, column=i + 1).value\n        col_letter = openpyxl.utils.get_column_letter(i + 1)\n        if values_match(exp_val, actual_val):\n            matched += 1\n        else:\n            exp_display = \"empty\" if exp_val is None else f\"'{exp_val}'\"\n            act_display = \"empty\" if cell_empty(actual_val) else f\"'{normalize(actual_val)}'\"\n            mismatches.append(f\"  Col {col_letter}: expected {exp_display}, found {act_display}\")\n\n    total = len(expected)\n    score = round(matched / total, 4)\n\n    if not mismatches:\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": \"Row 5 matches all 31 expected values.\"}\n\n    return {\n        \"pass\": False,\n        \"score\": score,\n        \"feedback\": f\"Row 5 has {len(mismatches)}/{total} field(s) wrong:\\n\" + \"\\n\".join(mismatches),\n    }",
    "criterion_type": "expected_output"
  },
  {
    "id": "rubric_1776277829339",
    "sort_order": 4,
    "rubric_text": "`inventory_master.xlsx` does not include the value `FL-08814-A1`.",
    "verifier_code": "from pathlib import Path\nimport openpyxl\n\n\ndef verify(workspace_path, external_services_path=None):\n    target_file = Path(workspace_path) / \"inventory_master.xlsx\"\n\n    if not target_file.exists():\n        return {\n            \"pass\": False,\n            \"score\": 0.0,\n            \"feedback\": f\"File 'inventory_master.xlsx' not found in workspace: {workspace_path}\"\n        }\n\n    search_value = \"FL-08814-A1\"\n    search_lower = search_value.strip().lower()\n\n    try:\n        wb = openpyxl.load_workbook(target_file, data_only=True)\n    except Exception as e:\n        return {\n            \"pass\": False,\n            \"score\": 0.0,\n            \"feedback\": f\"Could not open 'inventory_master.xlsx': {e}\"\n        }\n\n    found_locations = []\n    for sheet_name in wb.sheetnames:\n        ws = wb[sheet_name]\n        for row_idx, row in enumerate(ws.iter_rows(values_only=False), start=1):\n            for cell in row:\n                if cell.value is not None:\n                    cell_str = str(cell.value).strip().lower()\n                    if cell_str == search_lower:\n                        found_locations.append(\n                            f\"Sheet '{sheet_name}', cell {cell.coordinate}, value='{cell.value}'\"\n                        )\n\n    wb.close()\n\n    if len(found_locations) == 0:\n        return {\n            \"pass\": True,\n            \"score\": 1.0,\n            \"feedback\": f\"PASS: 'FL-08814-A1' was NOT found anywhere in 'inventory_master.xlsx', which is the expected result.\"\n        }\n    else:\n        locations_str = \"; \".join(found_locations)\n        return {\n            \"pass\": False,\n            \"score\": 0.0,\n            \"feedback\": f\"FAIL: 'FL-08814-A1' was found in 'inventory_master.xlsx' at the following location(s): {locations_str}. The rubric requires it NOT to be present.\"\n        }\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "rubric_1776277830559",
    "sort_order": 5,
    "rubric_text": "`inventory_master.xlsx`: has 67 rows (2 header rows, 65 data rows).",
    "verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n    file_path = Path(workspace_path) / \"inventory_master.xlsx\"\n    \n    if not file_path.exists():\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"File 'inventory_master.xlsx' not found in workspace at {workspace_path}.\"}\n    \n    try:\n        wb = openpyxl.load_workbook(file_path, data_only=True)\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Could not open 'inventory_master.xlsx': {e}\"}\n    \n    ws = wb.active\n    \n    # Count all rows that have at least one non-empty cell\n    total_rows = 0\n    for row in ws.iter_rows(min_row=1, max_row=ws.max_row):\n        if any(cell.value is not None for cell in row):\n            total_rows += 1\n    \n    # Also get ws.max_row for reference\n    max_row = ws.max_row\n    \n    expected_total = 67\n    # Be generous: accept 67 total rows exactly, or allow a small tolerance\n    # The rubric says 67 rows total (2 header + 65 data)\n    # We check the total non-empty row count\n    \n    feedback_details = f\"Found {total_rows} non-empty rows (ws.max_row={max_row}) in 'inventory_master.xlsx'. Expected {expected_total} total rows (2 header + 65 data).\"\n    \n    if total_rows == expected_total:\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": f\"PASS: {feedback_details}\"}\n\n    return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"FAIL: {feedback_details}\"}\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "rubric_1776277841819",
    "sort_order": 6,
    "rubric_text": "In `inventory_master.xlsx`, row 67 must contain the following values: FL-08814-A0 | D-105 | 0 | AVAILABLE | 10/14/2026 | REC-20261014-003 | (remaining cells empty).",
    "verifier_code": "from pathlib import Path\nimport re\nimport openpyxl\nfrom datetime import datetime\n\ndef verify(workspace_path, external_services_path=None):\n    file_path = Path(workspace_path) / \"inventory_master.xlsx\"\n    if not file_path.exists():\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": \"inventory_master.xlsx not found in workspace.\"}\n\n    try:\n        wb = openpyxl.load_workbook(str(file_path), data_only=True)\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Could not open inventory_master.xlsx: {e}\"}\n\n    ws = wb.active\n\n    # Expected values for row 67 (columns 1–11).\n    # None = cell must be empty.\n    expected = [\n        \"FL-08814-A0\",       # 1\n        \"D-105\",             # 2\n        0,                   # 3 (Quantity)\n        \"AVAILABLE\",         # 4\n        \"10/14/2026\",        # 5 (Date)\n        \"REC-20261014-003\",  # 6\n        None,                # 7\n        None,                # 8\n        None,                # 9\n        None,                # 10\n        None,                # 11\n    ]\n\n    def cell_empty(val):\n        return val is None or str(val).strip() == \"\"\n\n    def normalize(val):\n        if val is None:\n            return \"\"\n        return re.sub(r'\\s+', ' ', str(val).strip())\n\n    def values_match(exp, actual):\n        if exp is None:\n            return cell_empty(actual)\n\n        if cell_empty(actual):\n            return False\n\n        exp_str = normalize(exp)\n        act_str = normalize(actual)\n\n        if exp_str.lower() == act_str.lower():\n            return True\n\n        # datetime objects from openpyxl (date-only column)\n        if isinstance(actual, datetime):\n            return actual.month == 10 and actual.day == 14 and actual.year == 2026\n\n        # Numeric exact match (e.g. quantity 0 stored as 0 or \"0\")\n        try:\n            return float(exp_str) == float(act_str)\n        except (ValueError, TypeError):\n            pass\n\n        return False\n\n    mismatches = []\n    matched = 0\n\n    for i, exp_val in enumerate(expected):\n        actual_val = ws.cell(row=67, column=i + 1).value\n        if values_match(exp_val, actual_val):\n            matched += 1\n        else:\n            exp_display = \"empty\" if exp_val is None else f\"'{exp_val}'\"\n            act_display = \"empty\" if cell_empty(actual_val) else f\"'{normalize(actual_val)}'\"\n            mismatches.append(f\"  Col {i + 1}: expected {exp_display}, found {act_display}\")\n\n    total = len(expected)\n    score = round(matched / total, 4)\n\n    if not mismatches:\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": \"Row 67 matches all 11 expected values.\"}\n\n    return {\n        \"pass\": False,\n        \"score\": score,\n        \"feedback\": f\"Row 67 has {len(mismatches)}/{total} field(s) wrong:\\n\" + \"\\n\".join(mismatches),\n    }\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "rubric_1776277870617",
    "sort_order": 7,
    "rubric_text": "`slack_data.json`: Channels C002, C003, C004, D001, D002, D003, D004, D005, D006, D008, D009, D010, and D011 have 0 messages.",
    "verifier_code": "from pathlib import Path\nimport json\n\ndef verify(workspace_path, external_services_path=None):\n    slack_file = None\n    for base in [external_services_path, workspace_path]:\n        if base is not None:\n            candidate = Path(base) / \"slack_data.json\"\n            if candidate.exists():\n                slack_file = candidate\n                break\n\n    if slack_file is None:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": \"slack_data.json not found.\"}\n\n    try:\n        with open(slack_file, \"r\", encoding=\"utf-8\") as f:\n            data = json.load(f)\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Could not read slack_data.json: {e}\"}\n\n    messages = data.get(\"messages\")\n    if not isinstance(messages, dict):\n        return {\n            \"pass\": False,\n            \"score\": 0.0,\n            \"feedback\": f\"Expected data['messages'] to be a dict keyed by channel ID. Got: {type(messages).__name__}. Top-level keys: {list(data.keys()) if isinstance(data, dict) else 'N/A'}\",\n        }\n\n    required_zero = [\n        \"C002\", \"C003\", \"C004\",\n        \"D001\", \"D002\", \"D003\", \"D004\", \"D005\", \"D006\",\n        \"D008\", \"D009\", \"D010\", \"D011\",\n    ]\n\n    failures = []\n    for ch_id in required_zero:\n        msgs = messages.get(ch_id, [])\n        count = len(msgs) if isinstance(msgs, list) else int(msgs)\n        if count != 0:\n            failures.append(f\"{ch_id}: expected 0 messages, found {count}\")\n\n    total = len(required_zero)\n    score = round((total - len(failures)) / total, 4)\n\n    if not failures:\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": \"All 13 channels have 0 messages as expected.\"}\n\n    return {\n        \"pass\": False,\n        \"score\": score,\n        \"feedback\": f\"{len(failures)}/{total} channel(s) have unexpected messages:\\n\" + \"\\n\".join(f\"  {f}\" for f in failures),\n    }\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "rubric_1776277884627",
    "sort_order": 8,
    "rubric_text": "`slack_data.json`: channel C001 has 5 messages.",
    "verifier_code": "from pathlib import Path\nimport json\n\ndef verify(workspace_path, external_services_path=None):\n    slack_file = None\n    for base in [external_services_path, workspace_path]:\n        if base is not None:\n            candidate = Path(base) / \"slack_data.json\"\n            if candidate.exists():\n                slack_file = candidate\n                break\n\n    if slack_file is None:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": \"slack_data.json not found.\"}\n\n    try:\n        with open(slack_file, \"r\", encoding=\"utf-8\") as f:\n            data = json.load(f)\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Could not read slack_data.json: {e}\"}\n\n    messages = data.get(\"messages\")\n    if not isinstance(messages, dict):\n        return {\n            \"pass\": False,\n            \"score\": 0.0,\n            \"feedback\": f\"Expected data['messages'] to be a dict keyed by channel ID. Got: {type(messages).__name__}.\",\n        }\n\n    c001 = messages.get(\"C001\", [])\n    if not isinstance(c001, list):\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"C001 messages is not a list; got {type(c001).__name__}.\"}\n\n    count = len(c001)\n\n    if count == 5:\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": \"Channel C001 has exactly 5 messages as expected.\"}\n\n    return {\n        \"pass\": False,\n        \"score\": 0.0,\n        \"feedback\": f\"Channel C001 has {count} message(s); expected exactly 5.\",\n    }\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "rubric_1776277884883",
    "sort_order": 9,
    "rubric_text": "`mailbox.json`: Has 0 emails in Sent.",
    "verifier_code": "from pathlib import Path\nimport json\n\ndef verify(workspace_path, external_services_path=None):\n    # Check external_services_path for mailbox.json\n    mailbox_path = None\n    if external_services_path:\n        candidate = Path(external_services_path) / \"mailbox.json\"\n        if candidate.exists():\n            mailbox_path = candidate\n    # Also check workspace_path as fallback\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 external_services_path or workspace_path.\"}\n    \n    try:\n        with open(mailbox_path, 'r', encoding='utf-8') 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    sent_emails = [e for e in emails if str(e.get(\"folder\", \"\")).strip().lower() == \"sent\"]\n    count = len(sent_emails)\n    \n    if count == 0:\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": f\"mailbox.json has 0 emails in Sent as expected.\"}\n    else:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Expected 0 emails in Sent, but found {count}. Subjects: {[e.get('subject','') for e in sent_emails]}\"}\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "rubric_1776277917163",
    "sort_order": 10,
    "rubric_text": "`slack_data.json`: Channel D007 has exactly 1 message with the following content:\n\nHi Maribel,\n\nA new SKU was received today that does not have an existing entry in inventory_master.xlsx. Please create the master record so I can complete the receipt.\n\nReceipt details:\n\n- Receipt ID: REC-20261124-003\n- PO Number: MER-PO-204871\n- Supplier: Vantage Lubricants Inc.\n- SKU: FL-08814-A1\n- SKU description (per packing slip): Gear oil, ISO VG 220, 20L drum\n- Quantity received: 24\n- Safety-Critical: N\n- Suggested location: C-RCV-01\n\nMaterial is currently held in the receiving area pending master record creation. Please confirm when the row has been added.\n\nThanks,\nJR",
    "verifier_code": "from pathlib import Path\nimport re\nimport json\nfrom collections import Counter\n\nEXPECTED_TEXT = \"\"\"\\\nHi Maribel,\n\nA new SKU was received today that does not have an existing entry in inventory_master.xlsx. Please create the master record so I can complete the receipt.\n\nReceipt details:\n\n- Receipt ID: REC-20261124-003\n- PO Number: MER-PO-204871\n- Supplier: Vantage Lubricants Inc.\n- SKU: FL-08814-A1\n- SKU description (per packing slip): Gear oil, ISO VG 220, 20L drum\n- Quantity received: 24\n- Safety-Critical: N\n- Suggested location: C-RCV-01\n\nMaterial is currently held in the receiving area pending master record creation. Please confirm when the row has been added.\n\nThanks,\nJR\"\"\"\n\nKEY_PHRASES = [\n    \"rec 20261124 003\",\n    \"mer po 204871\",\n    \"vantage lubricants inc\",\n    \"fl 08814 a1\",\n    \"gear oil iso vg 220 20l drum\",\n    \"quantity received 24\",\n    \"safety critical n\",\n    \"c rcv 01\",\n    \"maribel\",\n    \"jr\",\n]\n\ndef normalize(text):\n    text = text.lower()\n    text = re.sub(r\"[^a-z0-9 ]\", \" \", text)\n    text = re.sub(r\"\\s+\", \" \", text)\n    return text.strip()\n\ndef verify(workspace_path, external_services_path=None):\n    slack_file = None\n    for base in [external_services_path, workspace_path]:\n        if base is not None:\n            candidate = Path(base) / \"slack_data.json\"\n            if candidate.exists():\n                slack_file = candidate\n                break\n\n    if slack_file is None:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": \"slack_data.json not found.\"}\n\n    try:\n        with open(slack_file, \"r\", encoding=\"utf-8\") as f:\n            data = json.load(f)\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Could not read slack_data.json: {e}\"}\n\n    messages_section = data.get(\"messages\")\n    if not isinstance(messages_section, dict):\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": \"data['messages'] is missing or not a dict.\"}\n\n    d007_msgs = messages_section.get(\"D007\", [])\n    if not isinstance(d007_msgs, list):\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"D007 messages is not a list; got {type(d007_msgs).__name__}.\"}\n\n    count = len(d007_msgs)\n    if count == 0:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": \"D007 has 0 messages; expected exactly 1.\"}\n    if count > 1:\n        return {\n            \"pass\": False,\n            \"score\": 0.0,\n            \"feedback\": f\"D007 has {count} messages; expected exactly 1. Duplicate sends are not permitted.\",\n        }\n\n    msg = d007_msgs[0]\n    actual_text = msg.get(\"text\", msg.get(\"message\", msg.get(\"body\", \"\"))) if isinstance(msg, dict) else str(msg)\n\n    exp_norm = normalize(EXPECTED_TEXT)\n    act_norm = normalize(actual_text)\n\n    # Pass if the full expected content is present in the actual message. This allows\n    # legitimate preamble such as a \"Subject:\" line (Template 18 in the SOP is itself\n    # drafted with a Subject line) and any trailing signature, while still requiring\n    # that every word of the template body appear in order.\n    if exp_norm in act_norm:\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": \"D007 message contains all expected Template 18 content (after normalisation).\"}\n\n    # Partial score: token-level recall\n    exp_tokens = Counter(exp_norm.split())\n    act_tokens = Counter(act_norm.split())\n    matched_tokens = sum((exp_tokens & act_tokens).values())\n    score = round(matched_tokens / sum(exp_tokens.values()), 4)\n\n    missing_phrases = [p for p in KEY_PHRASES if p not in act_norm]\n    extra_note = f\"Missing key fields: {missing_phrases}\" if missing_phrases else \"All key fields present — mismatch is in boilerplate wording.\"\n\n    return {\n        \"pass\": False,\n        \"score\": score,\n        \"feedback\": (\n            f\"D007 message does not contain expected content (token recall: {score:.1%}).\\n\"\n            f\"{extra_note}\\n\"\n            f\"Expected (normalised):\\n  {exp_norm}\\n\"\n            f\"Actual (normalised):\\n  {act_norm}\"\n        ),\n    }",
    "criterion_type": "expected_output"
  },
  {
    "id": "rubric_1776720465519",
    "sort_order": 11,
    "rubric_text": "In `dock_schedule.xlsx`, row 5 must contain the following values in order: APPT-20261124-003 | 11/24/2026 | 12:00 CST | 13:30 CST | Dock 2 | XPO Logistics | SUP-0147 | Vantage Lubricants Inc. | MER-PO-204871 | 1056.0 | N | (empty) | 13:30 CST | (empty) | Arrived",
    "verifier_code": "from pathlib import Path\nimport openpyxl\nimport re\n\ndef verify(workspace_path, external_services_path=None):\n    file_path = Path(workspace_path) / \"dock_schedule.xlsx\"\n    if not file_path.exists():\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"File dock_schedule.xlsx not found at {file_path}\"}\n\n    try:\n        wb = openpyxl.load_workbook(str(file_path))\n        ws = wb.active\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Error opening dock_schedule.xlsx: {e}\"}\n\n    # Row 5\n    row_idx = 5\n    row_values = []\n    for col in range(1, ws.max_column + 1):\n        cell = ws.cell(row=row_idx, column=col)\n        row_values.append(cell.value)\n\n    if not row_values or len(row_values) == 0:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Row 5 is empty or does not exist.\"}\n\n    # Expected values in order (15 columns)\n    # Index:  0                    1            2           3           4       5               6         7                       8               9       10  11    12          13    14\n    # Values: APPT-20261124-003 | 11/24/2026 | 12:00 CST | 13:30 CST | Dock 2| XPO Logistics | SUP-0147| Vantage Lubricants Inc.| MER-PO-204871 | 1056.0 | N | '' | 13:30 CST | '' | Arrived\n\n    expected = [\n        \"APPT-20261124-003\",\n        \"11/24/2026\",\n        \"12:00 CST\",\n        \"13:30 CST\",\n        \"Dock 2\",\n        \"XPO Logistics\",\n        \"SUP-0147\",\n        \"Vantage Lubricants Inc.\",\n        \"MER-PO-204871\",\n        1056.0,\n        \"N\",\n        None,  # empty\n        \"13:30 CST\",\n        None,  # empty\n        \"Arrived\"\n    ]\n\n    def normalize(val):\n        if val is None:\n            return \"\"\n        s = str(val).strip()\n        return s\n\n    def is_empty(val):\n        if val is None:\n            return True\n        s = str(val).strip()\n        return s == \"\" or s.lower() == \"none\"\n\n    def nums_close(a, b, tol=0.05):\n        try:\n            fa = float(a)\n            fb = float(b)\n            if fb == 0:\n                return abs(fa) < 0.01\n            return abs(fa - fb) / max(abs(fb), 1e-9) <= tol\n        except:\n            return False\n\n    def match_date(actual, expected_str):\n        # expected_str is \"11/24/2026\"\n        # actual could be a datetime object or string\n        import datetime\n        norm_a = normalize(actual)\n        if norm_a == expected_str:\n            return True\n        # Try to parse as date\n        try:\n            if hasattr(actual, 'strftime'):\n                formatted = actual.strftime('%m/%d/%Y')\n                if formatted == expected_str:\n                    return True\n                formatted2 = actual.strftime('%-m/%-d/%Y')\n                if formatted2 == expected_str:\n                    return True\n        except:\n            pass\n        # Try flexible string matching\n        # Accept 2026-11-24 etc.\n        if \"2026\" in norm_a and \"11\" in norm_a and \"24\" in norm_a:\n            return True\n        return False\n\n    def match_str(actual, expected_str):\n        na = normalize(actual).lower()\n        ne = expected_str.lower().strip()\n        if na == ne:\n            return True\n        # Remove extra whitespace\n        na_clean = re.sub(r'\\s+', ' ', na)\n        ne_clean = re.sub(r'\\s+', ' ', ne)\n        if na_clean == ne_clean:\n            return True\n        return False\n\n    # Pad row_values if shorter\n    while len(row_values) < 15:\n        row_values.append(None)\n\n    issues = []\n    # Column 0: APPT-20261124-003\n    if not match_str(row_values[0], expected[0]):\n        issues.append(f\"Col 1: expected '{expected[0]}', got '{row_values[0]}'\")\n\n    # Column 1: date 11/24/2026\n    if not match_date(row_values[1], expected[1]):\n        issues.append(f\"Col 2: expected '{expected[1]}', got '{row_values[1]}'\")\n\n    # Column 2: 12:00 CST\n    if not match_str(row_values[2], expected[2]):\n        issues.append(f\"Col 3: expected '{expected[2]}', got '{row_values[2]}'\")\n\n    # Column 3: 13:30 CST\n    if not match_str(row_values[3], expected[3]):\n        issues.append(f\"Col 4: expected '{expected[3]}', got '{row_values[3]}'\")\n\n    # Column 4: Dock 2\n    if not match_str(row_values[4], expected[4]):\n        issues.append(f\"Col 5: expected '{expected[4]}', got '{row_values[4]}'\")\n\n    # Column 5: XPO Logistics\n    if not match_str(row_values[5], expected[5]):\n        issues.append(f\"Col 6: expected '{expected[5]}', got '{row_values[5]}'\")\n\n    # Column 6: SUP-0147\n    if not match_str(row_values[6], expected[6]):\n        issues.append(f\"Col 7: expected '{expected[6]}', got '{row_values[6]}'\")\n\n    # Column 7: Vantage Lubricants Inc.\n    if not match_str(row_values[7], expected[7]):\n        issues.append(f\"Col 8: expected '{expected[7]}', got '{row_values[7]}'\")\n\n    # Column 8: MER-PO-204871\n    if not match_str(row_values[8], expected[8]):\n        issues.append(f\"Col 9: expected '{expected[8]}', got '{row_values[8]}'\")\n\n    # Column 9: 1056.0 (numeric)\n    if not nums_close(normalize(row_values[9]), 1056.0):\n        issues.append(f\"Col 10: expected '1056.0', got '{row_values[9]}'\")\n\n    # Column 10: N\n    if not match_str(row_values[10], \"N\"):\n        issues.append(f\"Col 11: expected 'N', got '{row_values[10]}'\")\n\n    # Column 11: empty\n    if not is_empty(row_values[11]):\n        issues.append(f\"Col 12: expected empty, got '{row_values[11]}'\")\n\n    # Column 12: 13:30 CST\n    if not match_str(row_values[12], \"13:30 CST\"):\n        issues.append(f\"Col 13: expected '13:30 CST', got '{row_values[12]}'\")\n\n    # Column 13: empty\n    if not is_empty(row_values[13]):\n        issues.append(f\"Col 14: expected empty, got '{row_values[13]}'\")\n\n    # Column 14: Arrived\n    if not match_str(row_values[14], \"Arrived\"):\n        issues.append(f\"Col 15: expected 'Arrived', got '{row_values[14]}'\")\n\n    if not issues:\n        return {\"pass\": True, \"score\": 1.0, \"feedback\": f\"Row 5 in dock_schedule.xlsx matches all expected values: {[normalize(v) for v in row_values[:15]]}\"}\n    else:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Row 5 in dock_schedule.xlsx has issues: {'; '.join(issues)}. Actual row values: {row_values[:15]}\"}\n",
    "criterion_type": "incorrect_behavior"
  },
  {
    "id": "rubric_1776721203166",
    "sort_order": 12,
    "rubric_text": "In `open_pos.xlsx`, row 5 must contain the following values in order: MER-PO-204871 | 001 | 10/31/2026 | 11/28/2026 | SUP-0147 | Vantage Lubricants Inc. | FL-08814-A1 | Gear oil, ISO VG 220, 20L drum | 24 | $38.50 | N | N | FL-08814-A0, FL-08814-A2, FL-08814-A3 | | | | | N | N | Open",
    "verifier_code": "from pathlib import Path\nimport openpyxl\nimport re\nfrom datetime import datetime\n\ndef verify(workspace_path, external_services_path=None):\n    file_path = Path(workspace_path) / \"open_pos.xlsx\"\n    if not file_path.exists():\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"File 'open_pos.xlsx' not found in workspace: {workspace_path}\"}\n\n    try:\n        wb = openpyxl.load_workbook(str(file_path))\n    except Exception as e:\n        return {\"pass\": False, \"score\": 0.0, \"feedback\": f\"Could not open 'open_pos.xlsx': {e}\"}\n\n    ws = wb.active\n\n    row_5 = []\n    for cell in ws[5]:\n        row_5.append(cell.value)\n\n    # Remove trailing None values\n    while row_5 and row_5[-1] is None:\n        row_5 = row_5[:-1]\n\n    # Expected values (20 columns):\n    # MER-PO-204871 | 001 | 10/31/2026 | 11/28/2026 | SUP-0147 | Vantage Lubricants Inc. | FL-08814-A1 | Gear oil, ISO VG 220, 20L drum | 24 | $38.50 | N | N | FL-08814-A0, FL-08814-A2, FL-08814-A3 | (empty) | (empty) | (empty) | (empty) | N | N | Open\n\n    expected = [\n        \"MER-PO-204871\",\n        \"001\",\n        \"10/31/2026\",\n        \"11/28/2026\",\n        \"SUP-0147\",\n        \"Vantage Lubricants Inc.\",\n        \"FL-08814-A1\",\n        \"Gear oil, ISO VG 220, 20L drum\",\n        \"24\",\n        \"$38.50\",\n        \"N\",\n        \"N\",\n        \"FL-08814-A0, FL-08814-A2, FL-08814-A3\",\n        \"\",\n        \"\",\n        \"\",\n        \"\",\n        \"N\",\n        \"N\",\n        \"Open\"\n    ]\n\n    def normalize(val):\n        if val is None:\n            return \"\"\n        if isinstance(val, datetime):\n            return val.strftime(\"%m/%d/%Y\")\n        s = str(val).strip()\n        return s\n\n    def values_match(actual_val, expected_val):\n        a = normalize(actual_val)\n        e = expected_val.strip()\n\n        # Both empty\n        if a == \"\" and e == \"\":\n            return True\n        if a == \"\" or e == \"\":\n            # If expected is empty, actual should be empty\n            return a == \"\" and e == \"\"\n\n        # Case-insensitive comparison\n        if a.lower() == e.lower():\n            return True\n\n        # Try numeric comparison with tolerance\n        def extract_number(s):\n            s_clean = re.sub(r'[\\$,\\s]', '', s)\n            try:\n                return float(s_clean)\n            except ValueError:\n                return None\n\n        a_num = extract_number(a)\n        e_num = extract_number(e)\n        if a_num is not None and e_num is not None:\n            if e_num == 0:\n                return abs(a_num) < 0.01\n            if abs(a_num - e_num) / max(abs(e_num), 0.001) <= 0.05:\n                return True\n\n        # For \"001\" vs \"1\" or \"1.0\"\n        if e == \"001\":\n            if a in [\"001\", \"1\", \"1.0\"]:\n                return True\n\n        # Date comparison: try multiple formats\n        date_formats = [\"%m/%d/%Y\", \"%Y-%m-%d\", \"%m-%d-%Y\", \"%d/%m/%Y\", \"%Y/%m/%d\", \"%m/%d/%y\"]\n        def parse_date(s):\n            for fmt in date_formats:\n                try:\n                    return datetime.strptime(s, fmt)\n                except ValueError:\n                    continue\n            return None\n\n        a_date = parse_date(a)\n        e_date = parse_date(e)\n        if a_date and e_date and a_date == e_date:\n            return True\n\n        # For the alternates field, compare sets of items\n        if \",\" in e:\n            e_parts = set(x.strip().lower() for x in e.split(\",\"))\n            a_parts = set(x.strip().lower() for x in a.split(\",\"))\n            if e_parts == a_parts:\n                return True\n\n        return False\n\n    # Ensure row has enough columns (pad with None if needed)\n    while len(row_5) < len(expected):\n        row_5.append(None)\n\n    mismatches = []\n    matches = 0\n    details = []\n    for i, (act, exp) in enumerate(zip(row_5[:len(expected)], expected)):\n        if values_match(act, exp):\n            matches += 1\n            details.append(f\"  Col {i+1}: OK (actual='{normalize(act)}', expected='{exp}')\")\n        else:\n            mismatches.append(i + 1)\n            details.append(f\"  Col {i+1}: MISMATCH (actual='{normalize(act)}', expected='{exp}')\")\n\n    total = len(expected)\n    score = matches / total if total > 0 else 0.0\n    passed = len(mismatches) == 0\n\n    detail_str = \"\\n\".join(details)\n    if passed:\n        feedback = f\"PASS: All {total} values in row 5 of open_pos.xlsx match expected values.\\n{detail_str}\"\n    else:\n        feedback = f\"FAIL: {len(mismatches)} of {total} values in row 5 do not match. Mismatched columns: {mismatches}\\n{detail_str}\"\n\n    return {\"pass\": passed, \"score\": score, \"feedback\": feedback}\n",
    "criterion_type": "incorrect_behavior"
  }
]
