from __future__ import annotations import json from pathlib import Path from models import HelpdeskTicketRecord from vocabulary import TASK_IDS TASKS = { 1: { "id": 1, "name": "Guided Full Routing", "difficulty": "easy", "instructions": ( "Perform full helpdesk routing by selecting issue type, priority, " "assignment group, and resolution action. Easy-task episodes keep the " "ticket text mostly visible and focus on grounded single-ticket routing." ), "allowed_fields": [ "issue_type", "priority", "assignment_group", "resolution_action", ], }, 2: { "id": 2, "name": "Contextual Full Routing", "difficulty": "medium", "instructions": ( "Perform full helpdesk routing with partial observability and moderate " "queue carry-over. Some tickets hide related-case, requester-history, " "or cluster-coordination details until you investigate or request more " "information, and medium episodes can also require deferral or coherent " "handling across linked tickets in the same queue." ), "allowed_fields": [ "issue_type", "priority", "assignment_group", "resolution_action", ], }, 3: { "id": 3, "name": "Adaptive Queue Routing", "difficulty": "hard", "instructions": ( "Perform full helpdesk routing by selecting the best issue type, " "priority, assignment group, and resolution action for the ticket. " "Use any ambiguity notes, related-ticket previews, queue-capacity " "forecasts, and planning state when present. " "Some hard tickets intentionally hide decisive routing context until " "you investigate with the available tools, and some hard episodes also " "require queue-level capacity planning, deferrals, incident management, " "and recovery from downstream follow-up tickets." ), "allowed_fields": [ "issue_type", "priority", "assignment_group", "resolution_action", ], }, } PLANNING_ROUTE_UPDATES: dict[str, dict] = { "ticket-022": { "service_cluster_id": "commerce_outage_recovery", "planning_note": ( "If the application queue is saturated, billing operations can own the " "customer-facing charge review as a lower-fidelity fallback while the bug " "investigation continues separately." ), "customer_update_note": ( "Finance confirmed the unexpected charge landed immediately after the " "integration outage and wants one accountable owner today." ), "alternate_issue_type": "billing_license", "alternate_assignment_group": "license_ops", "alternate_resolution_action": "assign", "alternate_route_score_multiplier": 0.74, }, "ticket-027": { "planning_note": ( "If procurement capacity is available, treat this like a commercial review. " "If not, a lightweight service-desk acknowledgement is still acceptable." ), "alternate_issue_type": "service_request", "alternate_priority": "medium", "alternate_assignment_group": "procurement", "alternate_resolution_action": "assign", "alternate_route_score_multiplier": 0.92, }, "ticket-029": { "planning_note": ( "Seat expansion is the preferred route, but license operations can still " "handle the prorating clarification when procurement is the bottleneck." ), "customer_update_note": ( "The requester clarified that the blocker is both the seat increase and " "the unexpected prorating language on the quote." ), "alternate_issue_type": "billing_license", "alternate_assignment_group": "license_ops", "alternate_resolution_action": "fulfill", "alternate_route_score_multiplier": 0.82, }, "ticket-040": { "planning_note": ( "The request can be treated either as roadmap feedback or as a support " "escalation if the operational impact is emphasized." ), "customer_update_note": ( "The requester says the missing behavior is now blocking a customer " "rollout, so this may need operational ownership rather than product triage." ), "alternate_issue_type": "application_support", "alternate_priority": "high", "alternate_resolution_action": "escalate", "alternate_route_score_multiplier": 0.76, }, "ticket-046": { "service_cluster_id": "atlasbank_lockout_bridge", }, "ticket-047": { "service_cluster_id": "bluequarry_launch_readiness", "planning_note": ( "The preferred route is an immediate service-desk extension, but the " "commercial owner can take it if operational fulfillment capacity is exhausted." ), "alternate_assignment_group": "procurement", "alternate_resolution_action": "assign", "alternate_route_score_multiplier": 0.78, }, "ticket-048": { "planning_note": ( "This belongs with procurement when commercial reviewers are available, " "but a generic service-desk acknowledgement is an acceptable fallback." ), "alternate_assignment_group": "service_desk", "alternate_resolution_action": "acknowledge", "alternate_route_score_multiplier": 0.9, }, "ticket-050": { "service_cluster_id": "merger_onboarding_wave", "planning_note": ( "Central coordination is preferred. If service-desk capacity is depleted, " "onboarding operations can still run a reduced fulfillment path." ), "alternate_priority": "medium", "alternate_assignment_group": "onboarding_ops", "alternate_resolution_action": "fulfill", "alternate_route_score_multiplier": 0.84, }, "ticket-051": { "planning_note": ( "Commercial procurement owns the contract amendment, but this can also " "be treated as a service request when the commercial queue needs triage." ), "alternate_issue_type": "service_request", "alternate_route_score_multiplier": 0.83, }, "ticket-052": { "service_cluster_id": "clientgrid_evidence_renewal", }, "ticket-053": { "planning_note": ( "Security scheduling is ideal, but a compliance acknowledgement is still " "acceptable when the security team only needs to confirm the process." ), "customer_update_note": ( "The requester clarified they mainly need confirmed ownership and a date " "for the review, not the review itself right now." ), "alternate_issue_type": "security_compliance", "alternate_resolution_action": "acknowledge", "alternate_route_score_multiplier": 0.8, }, "ticket-054": { "planning_note": ( "License operations can fulfill the archive request directly. If that queue " "is saturated, service desk can acknowledge and queue the retrieval." ), "alternate_assignment_group": "service_desk", "alternate_resolution_action": "acknowledge", "alternate_route_score_multiplier": 0.9, }, "ticket-038": { "service_cluster_id": "commerce_outage_recovery", }, } CURATED_EXPANSION_RECORDS: list[dict] = [ { "ticket_id": "ticket-056", "title": "Vendor DPA redlines need an owner before pricing sign-off", "requester": "procurement@harborcompliance.io", "description": ( "Commercial review is already moving, but the team needs to know who owns " "the vendor DPA redlines before pricing can be approved." ), "issue_type": "general_inquiry", "priority": "medium", "assignment_group": "procurement", "resolution_action": "assign", "planning_note": ( "Procurement is preferred, but service desk can acknowledge and route the " "questionnaire logistics if the commercial queue is saturated." ), "alternate_assignment_group": "service_desk", "alternate_resolution_action": "acknowledge", "alternate_route_score_multiplier": 0.9, }, { "ticket_id": "ticket-057", "title": "Board audit packet needs a timeline for the privileged-account lockout", "requester": "security-ops@atlasbank.io", "description": ( "Following up on ticket-046. The board pack needs a timeline and ownership " "summary for the privileged admin lockout before tomorrow morning." ), "issue_type": "identity_access", "priority": "high", "assignment_group": "security_team", "resolution_action": "escalate", "related_ticket_id": "ticket-046", "planning_note": ( "Security still owns the privileged-access review, but service desk can " "collect chronology and prepare the packet if the security queue is jammed." ), "customer_update_note": ( "Executives want a single incident bridge owner before the board packet is sent." ), "incident_recommended": True, "service_cluster_id": "atlasbank_lockout_bridge", "alternate_assignment_group": "service_desk", "alternate_resolution_action": "assign", "alternate_route_score_multiplier": 0.72, }, { "ticket_id": "ticket-058", "title": "Temporary contractor extension during an onboarding surge", "requester": "hr@talentbridge.co", "description": ( "A contractor start date slipped by two weeks and the account needs to stay " "active while the onboarding backlog is already full." ), "issue_type": "onboarding", "priority": "medium", "assignment_group": "service_desk", "resolution_action": "assign", "planning_note": ( "Service desk is preferred for cross-team coordination. If coordination " "capacity is exhausted, onboarding operations can fulfill the extension directly." ), "service_cluster_id": "merger_onboarding_wave", "alternate_assignment_group": "onboarding_ops", "alternate_resolution_action": "fulfill", "alternate_route_score_multiplier": 0.85, }, { "ticket_id": "ticket-059", "title": "Archived invoice packet plus quarter-close clarification", "requester": "boardops@silverpine.com", "description": ( "Finance needs archived invoice PDFs plus a quick note explaining whether any " "quarter-close adjustments are still pending." ), "issue_type": "general_inquiry", "priority": "medium", "assignment_group": "license_ops", "resolution_action": "fulfill", "planning_note": ( "Invoice operations can fulfill directly. If that queue is constrained, " "service desk can acknowledge and schedule the retrieval." ), "service_cluster_id": "commerce_outage_recovery", "alternate_assignment_group": "service_desk", "alternate_resolution_action": "acknowledge", "alternate_route_score_multiplier": 0.88, }, { "ticket_id": "ticket-060", "title": "Re: Temporary sandbox extension for the signed pilot", "requester": "solutions@bluequarry.io", "description": ( "Following up on ticket-047. The customer launch rehearsal is tomorrow, so the " "sandbox extension needs either immediate execution or a commercial owner to unblock it." ), "issue_type": "service_request", "priority": "high", "assignment_group": "service_desk", "resolution_action": "escalate", "related_ticket_id": "ticket-047", "planning_note": ( "Immediate operational execution is preferred. Procurement can still own the " "approval path if service-desk capacity is already depleted." ), "customer_update_note": ( "The customer says the launch rehearsal will fail without a same-day answer." ), "incident_recommended": True, "service_cluster_id": "bluequarry_launch_readiness", "alternate_assignment_group": "procurement", "alternate_resolution_action": "assign", "alternate_route_score_multiplier": 0.8, }, { "ticket_id": "ticket-061", "title": "Risk-exception review is blocking an SSO restore", "requester": "identity-risk@sterlingmed.io", "description": ( "Users cannot log in through SSO until a temporary risk exception is approved. " "The product team may need logs, but the unblock decision is tied to the review." ), "issue_type": "identity_access", "priority": "critical", "assignment_group": "security_team", "resolution_action": "escalate", "planning_note": ( "Security owns the final unblock decision. If security is saturated, the " "application team can still take the first-response diagnostics path." ), "customer_update_note": ( "The identity-risk lead confirmed users remain locked out and wants incident " "coordination while the exception is reviewed." ), "incident_recommended": True, "alternate_issue_type": "application_support", "alternate_priority": "high", "alternate_assignment_group": "application_team", "alternate_resolution_action": "escalate", "alternate_route_score_multiplier": 0.74, }, { "ticket_id": "ticket-062", "title": "Need product remediation evidence for a customer security questionnaire", "requester": "assurance@clientgrid.com", "description": ( "A customer questionnaire asks for evidence that a previously remediated " "application vulnerability is fully closed." ), "issue_type": "security_compliance", "priority": "medium", "assignment_group": "application_team", "resolution_action": "fulfill", "planning_note": ( "Application engineering is preferred because they hold the remediation artifacts. " "Security can still acknowledge the questionnaire and buy time when app capacity is tight." ), "service_cluster_id": "clientgrid_evidence_renewal", "alternate_assignment_group": "security_team", "alternate_resolution_action": "acknowledge", "alternate_route_score_multiplier": 0.82, }, { "ticket_id": "ticket-063", "title": "Subsidiary admin training with a seat-transfer request", "requester": "enablement@globalcorp.com", "description": ( "A newly acquired subsidiary needs admin training next week and also wants " "to transfer existing seats into the parent contract." ), "issue_type": "service_request", "priority": "medium", "assignment_group": "procurement", "resolution_action": "assign", "planning_note": ( "Procurement owns the commercial transfer. If that queue is overloaded, " "onboarding operations can still deliver the training portion first." ), "alternate_issue_type": "onboarding", "alternate_assignment_group": "onboarding_ops", "alternate_resolution_action": "fulfill", "alternate_route_score_multiplier": 0.78, }, { "ticket_id": "ticket-064", "title": "Legal-hold export of invoice history", "requester": "legalops@northshoreenergy.com", "description": ( "Legal needs invoice history exported for a hold notice. No pricing change is " "required, but the request must be acknowledged today." ), "issue_type": "general_inquiry", "priority": "high", "assignment_group": "license_ops", "resolution_action": "fulfill", "planning_note": ( "License operations can deliver the export. If they are capacity-constrained, " "service desk can acknowledge the request and queue the retrieval." ), "alternate_assignment_group": "service_desk", "alternate_resolution_action": "acknowledge", "alternate_route_score_multiplier": 0.87, }, { "ticket_id": "ticket-065", "title": "Cross-functional launch checklist for an acquired support team", "requester": "integration@mergerco.com", "description": ( "Twelve support agents from an acquired business need onboarding, mailbox " "setup, and a security attestation before Monday." ), "issue_type": "onboarding", "priority": "high", "assignment_group": "service_desk", "resolution_action": "assign", "planning_note": ( "Central coordination is preferred. If service-desk capacity is exhausted, " "onboarding operations can still run a reduced fulfillment path." ), "service_cluster_id": "merger_onboarding_wave", "alternate_priority": "medium", "alternate_assignment_group": "onboarding_ops", "alternate_resolution_action": "fulfill", "alternate_route_score_multiplier": 0.81, }, { "ticket_id": "ticket-066", "title": "Pilot customer asks who approves a credential-defense allowlist", "requester": "pilotops@cruxsystems.io", "description": ( "A pilot customer needs to know who approves an IP allowlist for a credential-" "defense control before they continue their test." ), "issue_type": "general_inquiry", "priority": "medium", "assignment_group": "security_team", "resolution_action": "assign", "planning_note": ( "Security should own the answer when available. If that queue is overloaded, " "service desk can acknowledge and route the ownership question." ), "alternate_assignment_group": "service_desk", "alternate_resolution_action": "acknowledge", "alternate_route_score_multiplier": 0.84, }, { "ticket_id": "ticket-067", "title": "Re: Remediation evidence package is now blocking a renewal signature", "requester": "assurance@clientgrid.com", "description": ( "Following up on ticket-052. Renewal signature is blocked until the remediation " "evidence package is delivered or a commercial owner confirms the delay." ), "issue_type": "security_compliance", "priority": "high", "assignment_group": "application_team", "resolution_action": "escalate", "related_ticket_id": "ticket-052", "planning_note": ( "Application engineering is preferred because they own the evidence. Procurement " "can still coordinate the renewal communication if the evidence queue is saturated." ), "customer_update_note": ( "Commercial leadership needs one named owner for the blocked renewal before end of day." ), "service_cluster_id": "clientgrid_evidence_renewal", "alternate_issue_type": "service_request", "alternate_priority": "medium", "alternate_assignment_group": "procurement", "alternate_resolution_action": "assign", "alternate_route_score_multiplier": 0.76, }, { "ticket_id": "ticket-068", "title": "Re: Second executive follow-up on the privileged-account lockout bridge", "requester": "security-ops@atlasbank.io", "description": ( "Another leadership update landed after ticket-057. Executives want to know " "whether one incident owner is already coordinating the privileged-account lockout." ), "issue_type": "identity_access", "priority": "high", "assignment_group": "security_team", "resolution_action": "escalate", "related_ticket_id": "ticket-057", "planning_note": ( "Security is still the preferred owner, but if an incident bridge is already " "running, service desk can acknowledge and consolidate the status update." ), "customer_update_note": ( "Leadership said they do not want a second parallel workstream for the same lockout." ), "incident_recommended": True, "service_cluster_id": "atlasbank_lockout_bridge", "alternate_assignment_group": "service_desk", "alternate_resolution_action": "acknowledge", "alternate_route_score_multiplier": 0.78, }, { "ticket_id": "ticket-069", "title": "Commercial sign-off still pending for the bluequarry launch rehearsal", "requester": "solutions@bluequarry.io", "description": ( "The sandbox extension is still the blocker, but finance now only needs one " "owner to confirm whether commercial approval or operational execution comes next." ), "issue_type": "service_request", "priority": "medium", "assignment_group": "procurement", "resolution_action": "assign", "related_ticket_id": "ticket-060", "planning_note": ( "Procurement owns the commercial answer, but if an earlier bridge is already " "active the service desk can acknowledge and link this follow-up into that track." ), "customer_update_note": ( "The customer only wants a single coordinated answer instead of separate procurement and support replies." ), "service_cluster_id": "bluequarry_launch_readiness", "alternate_assignment_group": "service_desk", "alternate_resolution_action": "acknowledge", "alternate_route_score_multiplier": 0.82, }, { "ticket_id": "ticket-070", "title": "Renewal counsel asks for a named owner on the remediation evidence package", "requester": "assurance@clientgrid.com", "description": ( "Legal asked whether the remediation evidence request from ticket-067 is now " "covered by an existing owner or needs a fresh commercial escalation." ), "issue_type": "security_compliance", "priority": "high", "assignment_group": "application_team", "resolution_action": "escalate", "related_ticket_id": "ticket-067", "planning_note": ( "Application engineering still owns the evidence, but a coordinated service-desk " "acknowledgement is acceptable if the earlier remediation thread already has an owner." ), "customer_update_note": ( "Counsel said duplicate answers from product and commercial teams would make the renewal risk look worse." ), "service_cluster_id": "clientgrid_evidence_renewal", "alternate_assignment_group": "service_desk", "alternate_resolution_action": "acknowledge", "alternate_route_score_multiplier": 0.8, }, { "ticket_id": "ticket-071", "title": "Access matrix still blocking the merger onboarding wave", "requester": "integration@mergerco.com", "description": ( "The onboarding launch from ticket-065 is still blocked because the access matrix " "for the acquired support team is incomplete and Monday is getting close." ), "issue_type": "onboarding", "priority": "high", "assignment_group": "service_desk", "resolution_action": "assign", "related_ticket_id": "ticket-065", "planning_note": ( "Central coordination remains preferred. If an earlier owner is already driving the " "wave, onboarding operations can fulfill the matrix updates while service desk handles the communications." ), "customer_update_note": ( "The integration lead wants one owner for the remaining blocker instead of a fresh handoff." ), "service_cluster_id": "merger_onboarding_wave", "alternate_assignment_group": "onboarding_ops", "alternate_resolution_action": "fulfill", "alternate_route_score_multiplier": 0.83, }, ] def _apply_dataset_enhancements( dataset: list[HelpdeskTicketRecord], ) -> list[HelpdeskTicketRecord]: enhanced_dataset: list[HelpdeskTicketRecord] = [] for record in dataset: update = PLANNING_ROUTE_UPDATES.get(record.ticket_id) enhanced_dataset.append( record.model_copy(update=update) if update is not None else record ) seen_ids = {record.ticket_id for record in enhanced_dataset} for raw_record in CURATED_EXPANSION_RECORDS: ticket_id = str(raw_record["ticket_id"]) if ticket_id in seen_ids: raise ValueError(f"Duplicate ticket_id in curated expansion: {ticket_id}") enhanced_dataset.append(HelpdeskTicketRecord.model_validate(raw_record)) seen_ids.add(ticket_id) return enhanced_dataset assert tuple(TASKS.keys()) == TASK_IDS def load_dataset() -> list[HelpdeskTicketRecord]: dataset_path = Path(__file__).resolve().parent.parent / "data" / "dataset.json" # Accept UTF-8 files saved with a BOM, which is common on Windows editors. with dataset_path.open("r", encoding="utf-8-sig") as f: raw = json.load(f) dataset = [HelpdeskTicketRecord.model_validate(r) for r in raw] return _apply_dataset_enhancements(dataset) def get_task_definition(task_id: int) -> dict: if task_id not in TASKS: raise ValueError(f"Unsupported task_id: {task_id}") return TASKS[task_id]