Task: workarena.servicenow.create-change-request
Goal: Create a new change request with a value of "A failed RAID controller card needs to be replaced on PSORA1" for field "Short description", a value of "" for field "Configuration item", a value of "CHG0000087" for field "Number", a value of "Fail PSORA1 to DR systemShutdown SystemRemove failed RAID cardInstall new RAID cardBoot systemConfigure RAID cardBring system onlineValidate access to disk structuresFailback from DR to PSORA1" for field "Implementation plan", a value of "Moderate" for field "Risk", a value of "slakeable voluptuate fetishmonger plebify fopling" for field "Close notes", a value of "" for field "Service", a value of "Database" for field "Assignment group", a value of "casein dihydroxytoluene stook arriba merist" for field "Risk and impact analysis", a value of "fiscalize pulwar hepatite upwreathe populicide" for field "Backout plan", a value of "Network" for field "Category", a value of "01:00.0 RAID bus controller: Adaptec AAC-RAID has failed on PSORA1 and needs to be replaced immediately." for field "Description", a value of "" for field "Service offering", a value of "Successful" for field "Close code", and a value of "2 - Medium" for field "Impact".
Task configuration: {'table_name': 'change_request', 'task_fields': ['short_description', 'cmdb_ci', 'number', 'implementation_plan', 'risk', 'close_notes', 'business_service', 'assignment_group', 'risk_impact_analysis', 'backout_plan', 'category', 'description', 'service_offering', 'close_code', 'impact'], 'template_record': {'active': 'true', 'activity_due': 'UNKNOWN', 'additional_assignee_list': '', 'approval': 'Requested', 'approval_history': '2023-10-08 06:14:56 - System Administrator (Approval history)\nGroup approval for Database approved by user Fred Luddy (Technical approvals).\n\n', 'approval_set': '', 'assigned_to': {'display_value': 'David Loo', 'link': 'https://dev215901.service-now.com/api/now/table/sys_user/5137153cc611227c000bbd1bd8cd2007'}, 'assignment_group': 'Database', 'backout_plan': 'fiscalize pulwar hepatite upwreathe populicide', 'business_duration': '', 'business_service': '', 'cab_date_time': '', 'cab_delegate': '', 'cab_recommendation': '', 'cab_required': 'false', 'calendar_duration': '', 'category': 'Network', 'change_plan': '', 'chg_model': '', 'close_code': 'Successful', 'close_notes': 'slakeable voluptuate fetishmonger plebify fopling', 'closed_at': '', 'closed_by': '', 'cmdb_ci': '', 'comments': '', 'comments_and_work_notes': '', 'company': '', 'conflict_last_run': '', 'conflict_status': 'Not Run', 'contact_type': None, 'contract': '', 'correlation_display': '', 'correlation_id': '', 'delivery_plan': '', 'delivery_task': '', 'description': '01:00.0 RAID bus controller: Adaptec AAC-RAID has failed on PSORA1 and needs to be replaced immediately.', 'due_date': '', 'end_date': '2024-01-03 06:30:00', 'escalation': 'Normal', 'expected_start': '', 'follow_up': '', 'group_list': '', 'impact': '2 - Medium', 'implementation_plan': 'Fail PSORA1 to DR systemShutdown SystemRemove failed RAID cardInstall new RAID cardBoot systemConfigure RAID cardBring system onlineValidate access to disk structuresFailback from DR to PSORA1', 'justification': 'witchery Moresque abeyant faggingly amphogenous', 'knowledge': 'false', 'location': '', 'made_sla': 'true', 'number': 'CHG0000087', 'on_hold': 'false', 'on_hold_reason': '', 'on_hold_task': '', 'opened_at': '2023-10-06 01:21:36', 'opened_by': {'display_value': 'System Administrator', 'link': 'https://dev215901.service-now.com/api/now/table/sys_user/6816f79cc0a8016401c5a33be04be441'}, 'order': '', 'outside_maintenance_schedule': 'false', 'parent': '', 'phase': None, 'phase_state': 'Open', 'priority': '2 - High', 'production_system': 'false', 'reason': None, 'reassignment_count': '0', 'requested_by': {'display_value': 'System Administrator', 'link': 'https://dev215901.service-now.com/api/now/table/sys_user/6816f79cc0a8016401c5a33be04be441'}, 'requested_by_date': '', 'review_comments': '', 'review_date': '', 'review_status': '', 'risk': 'Moderate', 'risk_impact_analysis': 'casein dihydroxytoluene stook arriba merist', 'route_reason': '', 'scope': 'Medium', 'service_offering': '', 'short_description': 'A failed RAID controller card needs to be replaced on PSORA1', 'sla_due': 'UNKNOWN', 'start_date': '2024-01-03 02:30:00', 'state': 'Authorize', 'std_change_producer_version': '', 'sys_class_name': 'Change Request', 'sys_created_by': 'admin', 'sys_created_on': '2023-10-06 01:21:36', 'sys_domain': {'display_value': 'global', 'link': 'https://dev215901.service-now.com/api/now/table/sys_user_group/global'}, 'sys_domain_path': '/', 'sys_id': '5acdb552db252200a6a2b31be0b8f5bc', 'sys_mod_count': '11', 'sys_tags': '', 'sys_updated_by': 'admin', 'sys_updated_on': '2023-10-09 01:54:06', 'task_effective_number': 'CHG0000087', 'test_plan': 'autistic coenobium consimilar perineotomy Babbitt', 'time_worked': '', 'type': 'Normal', 'unauthorized': 'false', 'universal_request': '', 'upon_approval': 'Proceed to Next Task', 'upon_reject': 'Cancel all future Tasks', 'urgency': '3 - Low', 'user_input': '', 'watch_list': '', 'work_end': '', 'work_notes': '', 'work_notes_list': '', 'work_start': '2023-10-08 02:48:26'}}

Validation code:
import json
import os
import re
import urllib.parse as parse
from typing import Any, Dict, List

import requests


def validate(messages: List[Dict[str, str]], config: Dict[str, Any]) -> int:
    """
    Validate that a record was created with expected field values.
    Returns 1 on success, else 0.

    Expected config keys:
      - task_fields (list[str])                  # fields to validate
      - template_record (dict)                   # expected display values for those fields
    """

    # Check whether we have the necessary information to validate
    print(config)
    if "table_name" not in config or "task_fields" not in config or "template_record" not in config:
        raise ValueError("Target must contain 'table_name', 'task_fields' and 'template_record'")

    # Check whether we have messages
    if not messages:
        raise ValueError("Messages must not be empty")

    # Extract last message content
    content = messages[-1].get("content", "")

    # Extract all URLs — markdown links first, then bare URLs
    seen = set()
    urls = []
    for m in re.finditer(r"\[.*?\]\((https?://[^)]+)\)", content):
        url = m.group(1)
        if url not in seen:
            seen.add(url)
            urls.append(url)
    for m in re.finditer(r"https?://\S+", content):
        url = m.group(0).rstrip(")].,")
        if url not in seen:
            seen.add(url)
            urls.append(url)
    if not urls:
        return 0

    def extract_sys_id(url: str) -> str | None:
        """
        Return the 32-hex sys_id from a ServiceNow URL, or None if not found.
        Handles encoded target URLs like .../params/target/incident.do%3Fsys_id%3D<id>
        """
        # Unquote repeatedly (some SN URLs are doubly-encoded)
        for _ in range(3):
            new = parse.unquote(url)
            if new == url:
                break
            url = new

        m = re.search(r"(?:[?&])sys_id=([0-9a-f]{32})", url, re.IGNORECASE)
        return m.group(1) if m else None

    sys_id = None
    for raw_url in urls:
        current_url = parse.urlunparse(parse.urlparse(parse.unquote(raw_url)))
        sys_id = extract_sys_id(current_url)
        if sys_id:
            break
    if not sys_id:
        return 0

    # Query instance to get record
    response = requests.request(
        method="GET",
        url=os.getenv("SERVICENOW_INSTANCE_URL", "").rstrip("/") + f"/api/now/table/{config['table_name']}",
        headers={
            "Content-Type": "application/json",
            "Accept": "application/json",
            **json.loads(os.getenv("SERVICENOW_EXTRA_HTTP_HEADERS", "{}")),
        },
        params={
            "sysparm_query": f"sys_id={sys_id}",
            "sysparm_display_value": "true",
            "sysparm_fields": ",".join(config["task_fields"]),
        },
    )
    response.raise_for_status()
    if type(response) == dict:
        record = response
    else:
        record = response.json()

    if "result" in record:
        record = record["result"]

    if isinstance(record, list):
        if len(record) == 0:
            return 0
        record = record[0]

    # Check that the record matches the expected values
    for f in config["task_fields"]:
        val = record.get(f)
        # Handle nested display_value dicts from the API
        if isinstance(val, dict) and "display_value" in val:
            val = val["display_value"]
        if val is None:
            return 0
        if val != config["template_record"][f]:
            return 0
    return 1