Files
handbook/tasks/logistics_prairie_star_creamery_f382586a/tests/rubrics.json
T
2026-06-24 14:16:58 -07:00

66 lines
30 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
[
{
"id": "f9149db2-34e8-4fe1-b769-5ff3bee0a859",
"sort_order": 0,
"rubric_text": "In file `inbound_receiving_log.xlsx`, row 13 must contain: Actual Pallets = 22, Actual Cases = 438, Temp Reading 1 = -4.5, Temp Reading 2 = -5, Temp Reading 3 = -8, Warmest Temp = -4.5, Temp Tier = 'Tier 3', Variance (Cases) = -2, Variance % = -0.45%, Damage Class = 'None', Lot Codes = 'A-20260411-01 (SC-001), A-20260411-02 (SC-006)', Disposition = 'Hold', Priority = 'P2', Status = 'Open — Pending P2 Assessment', Notes containing '-2' and 'Third Repeated Minor Variance Pattern - P3'",
"verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n xlsx_files = list(Path(workspace_path).glob('*inbound*receiving*log*.xlsx')) + list(Path(workspace_path).glob('*receiving*log*.xlsx'))\n if not xlsx_files:\n xlsx_files = [f for f in Path(workspace_path).glob('*.xlsx') if 'inbound' in f.name.lower() or 'receiving' in f.name.lower()]\n if not xlsx_files:\n all_xlsx = list(Path(workspace_path).glob('*.xlsx'))\n if not all_xlsx:\n return {'pass': False, 'score': 0.0, 'feedback': 'No xlsx files found in workspace'}\n xlsx_files = all_xlsx\n\n target_file = None\n for f in xlsx_files:\n if 'inbound' in f.name.lower() and 'receiving' in f.name.lower():\n target_file = f\n break\n if not target_file:\n target_file = xlsx_files[0]\n\n try:\n wb = openpyxl.load_workbook(target_file, data_only=True)\n except Exception as e:\n return {'pass': False, 'score': 0.0, 'feedback': f'Could not open {target_file.name}: {e}'}\n\n ws = wb.active\n row_data = [ws.cell(row=13, column=c).value for c in range(1, ws.max_column + 1)]\n\n # Column layout (0-indexed):\n # 0=Receipt Date, 1=Manifest/PO#, 2=Origin, 3=Site,\n # 4=Manifest Pallets, 5=Manifest Cases,\n # 6=Actual Pallets, 7=Actual Cases,\n # 8=Temp1, 9=Temp2, 10=Temp3,\n # 11=Warmest Temp (may be formula -> None),\n # 12=Temp Tier,\n # 13=Variance Cases (may be formula -> None),\n # 14=Variance % (may be formula -> None),\n # 15=Damage Class, 16=Lot Codes, 17=Disposition,\n # 18=Priority, 19=Status, 20=Notes\n\n # Resolve formula-backed cells from their source columns when cached value is None\n def get_warmest(row):\n if row[11] is not None:\n return row[11]\n temps = [row[i] for i in [8, 9, 10] if row[i] is not None]\n try:\n return max(float(t) for t in temps) if temps else None\n except (TypeError, ValueError):\n return None\n\n def get_variance_cases(row):\n if row[13] is not None:\n return row[13]\n mc, ac = row[5], row[7]\n try:\n return float(ac) - float(mc) if mc is not None and ac is not None else None\n except (TypeError, ValueError):\n return None\n\n def get_variance_pct(row):\n if row[14] is not None:\n return row[14]\n mc, ac = row[5], row[7]\n try:\n mc_f, ac_f = float(mc), float(ac)\n return (ac_f - mc_f) / mc_f if mc_f != 0 else None\n except (TypeError, ValueError):\n return None\n\n warmest = get_warmest(row_data)\n variance_cases = get_variance_cases(row_data)\n variance_pct = get_variance_pct(row_data)\n\n checks = []\n failures = []\n\n # Numeric checks\n numeric_checks = [\n (22, 'Actual Pallets', row_data[6]),\n (438, 'Actual Cases', row_data[7]),\n (-4.5, 'Temp Reading 1', row_data[8]),\n (-5, 'Temp Reading 2', row_data[9]),\n (-8, 'Temp Reading 3', row_data[10]),\n (-4.5, 'Warmest Temp', warmest),\n (-2, 'Variance Cases', variance_cases),\n ]\n for expected_val, label, actual_val in numeric_checks:\n try:\n found = actual_val is not None and abs(float(actual_val) - float(expected_val)) < 0.1\n except (TypeError, ValueError):\n found = False\n if found:\n checks.append(label)\n else:\n failures.append(f'{label} (expected {expected_val}, got {repr(actual_val)})')\n\n # Variance %: -0.45% stored as -0.0045 or -0.45\n vp_ok = False\n if variance_pct is not None:\n try:\n fv = float(variance_pct)\n vp_ok = abs(fv - (-0.0045)) < 0.0005 or abs(fv - (-0.45)) < 0.05\n except (TypeError, ValueError):\n vp_ok = '-0.45' in str(variance_pct)\n if not vp_ok and variance_pct is None:\n vp_ok = '-0.45' in str(row_data[14]) if row_data[14] is not None else False\n if vp_ok:\n checks.append('Variance %')\n else:\n failures.append(f'Variance % (expected -0.45%, got {repr(variance_pct)})')\n\n # String field checks — read specific columns directly, not row-wide concatenation\n def check_cell(col_idx, targets, label):\n val = row_data[col_idx]\n val_str = str(val).strip().lower() if val is not None else ''\n if any(t.lower() in val_str for t in targets):\n checks.append(label)\n else:\n failures.append(f'{label} (expected one of {targets}, got {repr(val)})')\n\n check_cell(12, ['tier 3'], 'Temp Tier')\n check_cell(15, ['none'], 'Damage Class')\n check_cell(17, ['hold'], 'Disposition')\n check_cell(18, ['p2'], 'Priority')\n\n # Status — check column 19 directly\n status_val = str(row_data[19]).lower() if row_data[19] is not None else ''\n if 'pending p2' in status_val or ('open' in status_val and 'p2' in status_val):\n checks.append('Status')\n else:\n failures.append(f'Status (expected Open — Pending P2 Assessment, got {repr(row_data[19])})')\n\n # Lot codes — check column 16 directly\n lot_val = str(row_data[16]).lower() if row_data[16] is not None else ''\n if 'a-20260411-01' in lot_val and 'a-20260411-02' in lot_val:\n checks.append('Lot Codes')\n else:\n failures.append(f'Lot Codes (expected A-20260411-01 and A-20260411-02, got {repr(row_data[16])})')\n\n # Notes — check column 20 directly for the required phrase and value\n notes_val = str(row_data[20]).lower() if row_data[20] is not None else ''\n if '-2' in notes_val and 'third repeated minor variance' in notes_val:\n checks.append('Notes')\n else:\n failures.append(f'Notes (expected \"-2\" and \"third repeated minor variance\" in Notes cell, got {repr(row_data[20])})')\n\n if not failures:\n return {'pass': True, 'score': 1.0,\n 'feedback': f'Row 13 in {target_file.name} contains all required fields: {checks}'}\n else:\n return {'pass': False, 'score': len(checks) / (len(checks) + len(failures)),\n 'feedback': f'Row 13 in {target_file.name} missing or incorrect fields: {failures}. Found: {checks}.'}",
"criterion_type": "expected_output"
},
{
"id": "9435bf97-bd03-40c7-9337-6ff1e27d209b",
"sort_order": 1,
"rubric_text": "In file `temperature_log.xlsx`, in sheet \"April 2026\", row 20 must contain Reading = -4.5 OR -4', row 21 must contain Reading = -5, and row 22 must contain Reading = -8",
"verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n # Find xlsx files broadly\n all_xlsx = list(Path(workspace_path).rglob('*.xlsx'))\n if not all_xlsx:\n return {'pass': False, 'score': 0.0, 'feedback': 'No xlsx files found in workspace'}\n\n # Try to find temperature_log.xlsx specifically, then fall back\n target_file = None\n for f in all_xlsx:\n if 'temperature' in f.name.lower() and 'log' in f.name.lower():\n target_file = f\n break\n if not target_file:\n for f in all_xlsx:\n if 'temp' in f.name.lower():\n target_file = f\n break\n if not target_file:\n target_file = all_xlsx[0]\n\n try:\n wb = openpyxl.load_workbook(target_file, data_only=True)\n except Exception as e:\n return {'pass': False, 'score': 0.0, 'feedback': f'Could not open {target_file.name}: {e}'}\n\n # Try to find the April 2026 sheet with flexible matching\n ws = None\n for name in wb.sheetnames:\n if 'april' in name.lower() and '2026' in name:\n ws = wb[name]\n break\n if ws is None:\n # Fall back to active sheet\n ws = wb.active\n\n # Determine which column contains \"Reading\" by scanning headers\n reading_col = None\n max_col = ws.max_column or 20\n\n # Scan first few rows for headers\n for header_row in range(1, min(6, (ws.max_row or 5) + 1)):\n for c in range(1, max_col + 1):\n val = ws.cell(row=header_row, column=c).value\n if val is not None:\n val_str = str(val).strip().lower()\n if 'reading' in val_str and 'within' not in val_str:\n reading_col = c\n\n # The rubric only checks Reading values for rows 20, 21, 22.\n # Row 20: Reading = -4.5 OR -4 (accept both)\n # Row 21: Reading = -5\n # Row 22: Reading = -8\n expected_rows = {\n 20: {'reading_options': [-4.5, -4.0]},\n 21: {'reading_options': [-5.0]},\n 22: {'reading_options': [-8.0]},\n }\n\n failures = []\n checks = []\n\n for row_num, expected in expected_rows.items():\n row_data = [ws.cell(row=row_num, column=c).value for c in range(1, max_col + 1)]\n row_str = [str(v).strip() if v is not None else '' for v in row_data]\n\n reading_found = False\n found_val = None\n\n def check_value(v, options):\n if v is None:\n return False, None\n try:\n fv = float(v)\n for opt in options:\n if abs(fv - opt) < 0.51:\n return True, fv\n except (ValueError, TypeError):\n # Also accept string representations like \"-4'\" or \"-4\\u2032\"\n v_str = str(v).strip().rstrip(\"'\\'\\u2032\\u2019\")\n try:\n fv = float(v_str)\n for opt in options:\n if abs(fv - opt) < 0.51:\n return True, fv\n except (ValueError, TypeError):\n pass\n return False, None\n\n # First check the identified reading column\n if reading_col is not None:\n v = ws.cell(row=row_num, column=reading_col).value\n reading_found, found_val = check_value(v, expected['reading_options'])\n\n # Fall back to scanning the whole row\n if not reading_found:\n for v in row_data:\n reading_found, found_val = check_value(v, expected['reading_options'])\n if reading_found:\n break\n\n if reading_found:\n checks.append(f'Row {row_num}: Reading={found_val} (expected one of {expected[\"reading_options\"]})')\n else:\n failures.append(f'Row {row_num}: Reading not found matching any of {expected[\"reading_options\"]}. Row data: {row_str}')\n\n if not failures:\n return {'pass': True, 'score': 1.0, 'feedback': f'All temperature log rows correct in {target_file.name} (sheet: {ws.title}): {checks}'}\n else:\n score = len(checks) / 3.0\n return {'pass': False, 'score': score, 'feedback': f'Temperature log failures in {target_file.name} (sheet: {ws.title}): {failures}. Passed: {checks}'}\n",
"criterion_type": "expected_output"
},
{
"id": "113fa31a-7a39-4aed-b8d8-13553c2272ca",
"sort_order": 2,
"rubric_text": "In `slack_data.json`, there must be a message in the #dc-escalations channel containing 'P2 escalation' and 'Manifest 2026-04-14-001'",
"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, 'feedback': 'external_services_path not provided; cannot check Slack data'}\n\n slack_path = Path(external_services_path) / 'slack_data.json'\n if not slack_path.exists():\n return {'pass': False, 'score': 0.0, 'feedback': f'slack_data.json not found at {slack_path}'}\n\n try:\n with open(slack_path, 'r') as f:\n data = json.load(f)\n except Exception as e:\n return {'pass': False, 'score': 0.0, 'feedback': f'Could not parse slack_data.json: {e}'}\n\n channels = data.get('channels', {})\n \n # Find escalation channel(s) - be loose: accept any channel with 'escalation' in the name\n escalation_channel_ids = []\n for cid, cinfo in channels.items():\n channel_name = cinfo.get('name', '').lower()\n if 'escalation' in channel_name or 'dc-escalation' in channel_name:\n escalation_channel_ids.append(cid)\n\n if not escalation_channel_ids:\n # Also try checking all channels in case the channel name is slightly different\n # Be generous: search all channels as a fallback\n escalation_channel_ids = list(channels.keys())\n if not escalation_channel_ids:\n return {'pass': False, 'score': 0.0, 'feedback': 'No channels found in slack_data.json'}\n\n messages = data.get('messages', {})\n\n # Required content - be loose with matching\n # 'P2 escalation' -> look for 'p2' and 'escalation' nearby or together\n # 'Manifest 2026-04-14-001' -> look for the manifest ID '2026-04-14-001'\n \n all_checked_messages = []\n \n for ch_id in escalation_channel_ids:\n channel_messages = messages.get(ch_id, [])\n for msg in channel_messages:\n text = msg.get('text', '')\n text_lower = text.lower()\n all_checked_messages.append(text[:150])\n \n # Check for P2 escalation - be loose\n has_p2_escalation = False\n if 'p2' in text_lower and 'escalation' in text_lower:\n has_p2_escalation = True\n elif 'p2 escalation' in text_lower:\n has_p2_escalation = True\n elif 'p-2' in text_lower and 'escalation' in text_lower:\n has_p2_escalation = True\n \n # Check for manifest ID - be loose\n has_manifest = False\n if '2026-04-14-001' in text:\n has_manifest = True\n elif 'manifest' in text_lower and '2026-04-14' in text_lower:\n has_manifest = True\n \n if has_p2_escalation and has_manifest:\n ch_name = channels.get(ch_id, {}).get('name', ch_id)\n return {\n 'pass': True,\n 'score': 1.0,\n 'feedback': f'Found P2 escalation message with Manifest 2026-04-14-001 in #{ch_name}. Message: {text[:200]}'\n }\n\n return {\n 'pass': False,\n 'score': 0.0,\n 'feedback': f'No message found containing both \"P2 escalation\" and \"Manifest 2026-04-14-001\" in escalation channels. Channels checked: {[channels.get(cid, {}).get(\"name\", cid) for cid in escalation_channel_ids]}. Messages found: {all_checked_messages[:10]}'\n }\n",
"criterion_type": "expected_output"
},
{
"id": "4bc4fafd-be20-45e5-86ca-471bd13a7b28",
"sort_order": 3,
"rubric_text": "In `mailbox.json`, there must be a sent email to daniel.reyes@prairiestar.com with carlos.mendoza@prairiestar.com and jacob.patel@prairiestar.com as CC, subject line 'P2 — Inbound Waco Manifest 2026-04-14-001 — 2026-04-14', containing '2026-04-14-001', 'put-away' (or 'put away'), and 'hold' in the body",
"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, 'feedback': 'external_services_path not provided; cannot check mailbox'}\n\n mailbox_path = Path(external_services_path) / 'mailbox.json'\n if not mailbox_path.exists():\n return {'pass': False, 'score': 0.0, 'feedback': f'mailbox.json not found at {mailbox_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'Could not parse mailbox.json: {e}'}\n\n emails = data.get('emails', [])\n sent_emails = [e for e in emails if e.get('folder', '').lower() == 'sent']\n\n for email in sent_emails:\n to_addr = email.get('to_addr', '').lower()\n cc_addr = email.get('cc_addr', '') or ''\n subject = email.get('subject', '')\n body = email.get('body_text', '').lower()\n\n if 'daniel.reyes@prairiestar.com' not in to_addr:\n continue\n\n # Check CC\n cc_lower = cc_addr.lower()\n if 'carlos.mendoza@prairiestar.com' not in cc_lower or 'jacob.patel@prairiestar.com' not in cc_lower:\n continue\n\n # Check subject (flexible match)\n subj_lower = subject.lower()\n if 'p2' not in subj_lower or '2026-04-14-001' not in subject or ('inbound' not in subj_lower and 'waco' not in subj_lower):\n continue\n\n # Check body content\n required_body = ['2026-04-14-001', 'hold']\n putaway_check = 'put-away' in body or 'put away' in body or 'putaway' in body\n body_ok = all(r.lower() in body for r in required_body) and putaway_check\n\n if body_ok:\n return {'pass': True, 'score': 1.0, 'feedback': f'Found P2 email to daniel.reyes@prairiestar.com with correct CC, subject, and body content'}\n else:\n missing_body = [r for r in required_body if r.lower() not in body]\n if not putaway_check:\n missing_body.append('put-away')\n return {'pass': False, 'score': 0.5, 'feedback': f'Found email to daniel.reyes with correct to/cc/subject but body missing: {missing_body}'}\n\n return {'pass': False, 'score': 0.0, 'feedback': f'No sent email found to daniel.reyes@prairiestar.com with correct CC (carlos.mendoza, jacob.patel) and subject containing P2 and 2026-04-14-001'}\n",
"criterion_type": "expected_output"
},
{
"id": "90eb9ad4-97f1-472e-bd9a-d85b8067d83d",
"sort_order": 4,
"rubric_text": "In `mailbox.json`, there must be an email sent to jacob.patel@prairiestar.com with subject line 'P3 Inbound Waco Manifest 2026-04-14-001 2026-04-14' and body containing all of the following: '2026-04-14-001', '-4.5', '-5', '-8', 'enhanced expiry monitoring', '-2', 'repeated minor variance', and 'explanation' (or equivalent).",
"verifier_code": "from pathlib import Path\nimport json\nimport re\n\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 not provided; cannot check mailbox'}\n\n mailbox_path = Path(external_services_path) / 'mailbox.json'\n if not mailbox_path.exists():\n return {'pass': False, 'score': 0.0,\n 'feedback': f'mailbox.json not found at {mailbox_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'Could not parse mailbox.json: {e}'}\n\n emails = data.get('emails', [])\n sent_emails = [e for e in emails if (e.get('folder', '') or '').lower() == 'sent']\n if not sent_emails:\n return {'pass': False, 'score': 0.0, 'feedback': 'No sent emails found in mailbox.json'}\n\n target_to = 'jacob.patel@prairiestar.com'\n\n # Normalize en-dash, em-dash, and hyphen all to plain hyphen for subject comparison\n def normalize_dashes(s):\n return re.sub(r'[\\u2013\\u2014\\-]', '-', s)\n\n expected_subject_norm = normalize_dashes(\n 'P3 - Inbound Waco Manifest 2026-04-14-001 - 2026-04-14'\n ).lower()\n\n required_body_terms = [\n '2026-04-14-001',\n '-4.5',\n '-5',\n '-8',\n 'enhanced expiry monitoring',\n '-2',\n 'repeated minor variance',\n ]\n explanation_equivalents = ['explanation', 'explain', 'rationale', 'reason', 'clarif', 'justif']\n\n def get_to_lower(email):\n to_addr = email.get('to_addr', '') or email.get('to', '') or ''\n if isinstance(to_addr, list):\n to_addr = ' '.join(to_addr)\n return to_addr.lower()\n\n diagnostics = []\n for email in sent_emails:\n to_lower = get_to_lower(email)\n if target_to not in to_lower:\n continue\n\n subject = email.get('subject', '') or ''\n subj_norm = normalize_dashes(subject).lower().strip()\n body = (email.get('body_text', '') or email.get('body', '') or '')\n body_lower = body.lower()\n\n subj_match = subj_norm == expected_subject_norm\n missing_body = [t for t in required_body_terms if t.lower() not in body_lower]\n has_expl = any(term in body_lower for term in explanation_equivalents)\n\n if subj_match and not missing_body and has_expl:\n return {'pass': True, 'score': 1.0,\n 'feedback': f'Found sent email to {target_to} with subject \"{subject}\" '\n f'and all required body content.'}\n\n diagnostics.append(\n f'Email subject \"{subject}\": subject_match={subj_match}, '\n f'missing_body={missing_body}, has_explanation_term={has_expl}'\n )\n\n if not diagnostics:\n return {'pass': False, 'score': 0.0,\n 'feedback': f'No sent emails found to {target_to}.'}\n return {'pass': False, 'score': 0.0,\n 'feedback': 'No sent email to {} satisfied all checks. {}'.format(\n target_to, ' | '.join(diagnostics))}\n",
"criterion_type": "expected_output"
},
{
"id": "780f301b-297a-48fb-8cfb-dd96a751d119",
"sort_order": 5,
"rubric_text": "In `mailbox.json`, there must NOT be a sent email to emily.carter@prairiestar.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': False, 'score': 0.0, 'feedback': 'external_services_path not provided; cannot check mailbox'}\n\n mailbox_path = Path(external_services_path) / 'mailbox.json'\n if not mailbox_path.exists():\n return {'pass': False, 'score': 0.0, 'feedback': f'mailbox.json not found at {mailbox_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'Could not parse mailbox.json: {e}'}\n\n emails = data.get('emails', [])\n sent_emails = [e for e in emails if e.get('folder', '').lower() == 'sent']\n\n emily_emails = [e for e in sent_emails if any('emily.carter@prairiestar.com' in (e.get(field, '') or '').lower() for field in ['to_addr', 'cc_addr', 'bcc_addr'])]\n\n if emily_emails:\n return {'pass': False, 'score': 0.0,\n 'feedback': f'Found {len(emily_emails)} sent email(s) to/cc/bcc emily.carter@prairiestar.com — this should not exist. Subjects: {[e.get(\"subject\") for e in emily_emails]}'}\n else:\n return {'pass': True, 'score': 1.0, 'feedback': 'No sent email to/cc/bcc emily.carter@prairiestar.com found — correct'}\n",
"criterion_type": "incorrect_behavior"
},
{
"id": "4dec73cb-3b3a-4871-9554-41287ace133a",
"sort_order": 6,
"rubric_text": "In `inventory_master.xlsx`, there must NOT be any row containing lot code 'A-20260411-01' or 'A-20260411-02'",
"verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n xlsx_files = list(Path(workspace_path).glob('*inventory*master*.xlsx')) + list(Path(workspace_path).glob('*inventory*.xlsx'))\n if not xlsx_files:\n xlsx_files = [f for f in Path(workspace_path).glob('*.xlsx') if 'inventory' in f.name.lower()]\n if not xlsx_files:\n xlsx_files = list(Path(workspace_path).glob('*.xlsx'))\n\n target_file = None\n for f in xlsx_files:\n if 'inventory' in f.name.lower() and 'master' in f.name.lower():\n target_file = f\n break\n if not target_file:\n for f in xlsx_files:\n if 'inventory' in f.name.lower():\n target_file = f\n break\n if not target_file:\n return {'pass': False, 'score': 0.0, 'feedback': 'Could not find inventory_master.xlsx in workspace'}\n\n try:\n wb = openpyxl.load_workbook(target_file, data_only=True)\n except Exception as e:\n return {'pass': False, 'score': 0.0, 'feedback': f'Could not open {target_file.name}: {e}'}\n\n forbidden = ['a-20260411-01', 'a-20260411-02']\n violations = []\n\n for ws in wb.worksheets:\n for row_idx, row in enumerate(ws.iter_rows(values_only=True), start=1):\n for cell_val in row:\n if cell_val is not None:\n cell_str = str(cell_val).lower().strip()\n for f_val in forbidden:\n if f_val in cell_str:\n violations.append(f'Sheet={ws.title}, Row={row_idx}, Value={cell_val}')\n\n if violations:\n return {'pass': False, 'score': 0.0,\n 'feedback': f'Found forbidden lot codes in {target_file.name}: {violations}'}\n else:\n return {'pass': True, 'score': 1.0,\n 'feedback': f'No rows with A-20260411-01 or A-20260411-02 found in {target_file.name} — correct'}\n",
"criterion_type": "incorrect_behavior"
},
{
"id": "fd88ec50-b68c-497c-ae9f-a8e6081e3ca1",
"sort_order": 7,
"rubric_text": "In `expiry_tracker.xlsx`, there must NOT be any row containing lot code 'A-20260411-01' or 'A-20260411-02'",
"verifier_code": "from pathlib import Path\nimport openpyxl\n\ndef verify(workspace_path, external_services_path=None):\n xlsx_files = list(Path(workspace_path).glob('*expiry*track*.xlsx')) + list(Path(workspace_path).glob('*expiry*.xlsx'))\n if not xlsx_files:\n xlsx_files = [f for f in Path(workspace_path).glob('*.xlsx') if 'expiry' in f.name.lower() or 'expir' in f.name.lower()]\n if not xlsx_files:\n xlsx_files = list(Path(workspace_path).glob('*.xlsx'))\n\n target_file = None\n for f in xlsx_files:\n name_lower = f.name.lower()\n if 'expiry' in name_lower and ('track' in name_lower or 'tracker' in name_lower):\n target_file = f\n break\n if not target_file:\n for f in xlsx_files:\n if 'expiry' in f.name.lower() or 'expir' in f.name.lower():\n target_file = f\n break\n if not target_file:\n return {'pass': False, 'score': 0.0, 'feedback': 'Could not find expiry_tracker.xlsx in workspace'}\n\n try:\n wb = openpyxl.load_workbook(target_file, data_only=True)\n except Exception as e:\n return {'pass': False, 'score': 0.0, 'feedback': f'Could not open {target_file.name}: {e}'}\n\n forbidden = ['a-20260411-01', 'a-20260411-02']\n violations = []\n\n for ws in wb.worksheets:\n for row_idx, row in enumerate(ws.iter_rows(values_only=True), start=1):\n for cell_val in row:\n if cell_val is not None:\n cell_str = str(cell_val).lower().strip()\n for f_val in forbidden:\n if f_val in cell_str:\n violations.append(f'Sheet={ws.title}, Row={row_idx}, Value={cell_val}')\n\n if violations:\n return {'pass': False, 'score': 0.0,\n 'feedback': f'Found forbidden lot codes in {target_file.name}: {violations}'}\n else:\n return {'pass': True, 'score': 1.0,\n 'feedback': f'No rows with A-20260411-01 or A-20260411-02 found in {target_file.name} — correct'}\n",
"criterion_type": "incorrect_behavior"
},
{
"id": "3d60a3bd-ac14-4934-8beb-024854b3877e",
"sort_order": 8,
"rubric_text": "In `mailbox.json`, there must be only one email with a subject line that contains 'P3' sent to jacob.patel@prairiestar.com.",
"verifier_code": "from pathlib import Path\nimport json\n\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 not provided; cannot check mailbox'}\n\n mailbox_path = Path(external_services_path) / 'mailbox.json'\n if not mailbox_path.exists():\n return {'pass': False, 'score': 0.0,\n 'feedback': f'mailbox.json not found at {mailbox_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'Could not parse mailbox.json: {e}'}\n\n emails = data.get('emails', [])\n sent_emails = [e for e in emails if (e.get('folder', '') or '').lower() == 'sent']\n\n target_to = 'jacob.patel@prairiestar.com'\n\n def get_to_lower(email):\n to_addr = email.get('to_addr', '') or email.get('to', '') or ''\n if isinstance(to_addr, list):\n to_addr = ' '.join(to_addr)\n return to_addr.lower()\n\n matches = []\n for email in sent_emails:\n subject = email.get('subject', '') or ''\n to_lower = get_to_lower(email)\n if 'p3' in subject.lower() and target_to in to_lower:\n matches.append(email.get('email_id') or subject)\n\n if len(matches) == 1:\n return {'pass': True, 'score': 1.0,\n 'feedback': f'Exactly one P3 email sent to {target_to} (matched: {matches[0]}).'}\n return {'pass': False, 'score': 0.0,\n 'feedback': f'Expected exactly 1 P3 email sent to {target_to}, found {len(matches)}. '\n f'Matches: {matches}'}\n",
"criterion_type": "expected_output"
}
]