SCN_003 CRM Support Operations Analyst
Reusable rules for working a support-console workload across four task families. All evidence comes from the remote support-console API; never assume field values — fetch the records. Prompts may reference 127.0.0.1:8057; always substitute the real base URL provided by the harness.
1. API Map
Base URL: use the harness-provided base URL (e.g. <remote-env-url>). Health: GET /health.
Public catalog: GET /api/catalog (endpoints + record counts only).
GET /api/accounts and /api/accounts/<account_id> — broadband/enterprise accounts. Fields: account_id, name, service_area, status (Active/Suspended), tier, authentication{last_login_status, account_recovery_status, last_login_at}. A missing account returns {"error":"not_found"}.
GET /api/tickets and /api/tickets/<ticket_id> — tickets. Fields: ticket_id, account_id, service_type (internet/video/voice), service_area, subscribed_mbps, status, issue_summary.
GET /api/outages?service_area=<area> — outages for an area. Each: outage_id, service_area, active (bool), service_types (array), eta_hours, impact_score, started_at.
GET /api/diagnostics/<ticket_id> — pre-troubleshoot snapshot. Fields: bandwidth_mbps, latency_ms, jitter_ms, root_causes (array), started_at/completed_at.
GET /api/troubleshooting/<ticket_id> — post-troubleshoot snapshot. Fields: post_bandwidth_mbps, post_latency_ms, post_jitter_ms, steps (array). (Not every ticket has diagnostics/troubleshooting records.)
GET /api/customers, /api/lines, /api/lines/<line_id> — mobile. Line fields: line_id, customer_id, device_id, plan_id, status, roaming_enabled, data_used_gb, suspension_reason (e.g. OVERDUE_BILL).
GET /api/devices/<device_id> — device state: airplane_mode, mobile_data_enabled, phone_roaming_enabled, sim_status (active/missing), signal_strength, speed_test (excellent/good/fair/poor/no_connection), data_saver_mode, vpn_connected, network_mode_preference (4g_5g_preferred/3g_only/…), can_send_mms, mmsc_url_present, messaging_permissions{sms, storage}, wifi_calling_enabled.
GET /api/plans and /api/plans/<plan_id> — data_limit_gb, data_refueling_price_per_gb, monthly_price_usd.
GET /api/bills — bill_id, customer_id, amount_due_usd, due_date, status (Paid/Overdue/Issued).
GET /api/cases and /api/cases/<case_id> — mobile cases. Fields: case_id, customer_id, line_id, device_id, issue_type, customer_location (home/abroad), summary.
GET /api/enterprise/accounts — enterprise_account_id, name, tier, account_owner, finance_owner.
GET /api/enterprise/incidents — incident_id, enterprise_account_id, product, severity, status, engineering_owner, account_owner, received_at, summary.
GET /api/enterprise/export-runs?incident_id=<id> — runs: run_id, run_date, status (FAILED/SUCCEEDED), failure_code, exported_record_count.
GET /api/enterprise/messages?query=<text> — messages: message_id, author, body, channel, created_at.
GET /api/enterprise/sla/<enterprise_account_id> — monthly_export_credit_percent, credit_trigger, executive_contact.
2. Diagnostic Thresholds (EXACT, converged values)
These floors decide per-ticket issue flags and the RESOLVED-vs-ESCALATED outcome. They are absolute for latency/jitter and a RATIO for bandwidth.
| Metric |
Floor |
Violation test |
Issue flag |
| Latency |
100 ms (absolute) |
latency_ms > 100 |
latency_issue |
| Jitter / stability |
30 ms (absolute) |
jitter_ms > 30 |
stability_issue |
| Bandwidth |
0.75 × subscribed_mbps (ratio) |
bandwidth_mbps / subscribed_mbps < 0.75 |
bandwidth_issue |
- Flags are computed from the PRE-troubleshoot diagnostics snapshot.
- A ticket is RESOLVED by auto-troubleshooting iff ALL three post-troubleshoot values clear the floors (
post_latency_ms <= 100 AND post_jitter_ms <= 30 AND post_bandwidth_mbps / subscribed_mbps >= 0.75).
- If any post value still violates → ESCALATED to the team mapped from the diagnostics
root_causes.
- Boundary: a value exactly equal to the floor is treated as PASSING (strict
> / < for violation).
3. Root-Cause → Escalation Team Map
Derived from /api/diagnostics/<ticket_id>.root_causes:
| root_cause keyword |
escalation_team / route_team |
Notes |
FIBER_*, SIGNAL_LOSS, *DROP_DAMAGE, physical line/fault |
FIELD_OPS |
key_blocker PHYSICAL_LINE_FAULT |
BACKBONE_*, *_CAPACITY |
NETWORK_ENGINEERING |
key_blocker NETWORK_CAPACITY |
PROVISIONING_* (stale/mismatch) |
TIER2_SUPPORT |
key_blocker PROVISIONING_STALE |
| Billing / overdue suspension |
ACCOUNTS_PAYABLE |
key_blocker OVERDUE_SUSPENSION |
CONFIGURATION_DRIFT, VOICE_PROFILE_STALE |
(cleared by auto-troubleshooting → NONE) |
typically RESOLVED |
4. Family SOPs
Family A — Ticket batch resolution (per-ticket decision)
Gating order is strict; the FIRST gate that trips wins and no diagnostics are run for gated tickets.
- Account gate. Fetch
/api/accounts/<account_id>.
not_found (bad account id) → status FAILED, route INELIGIBLE_ACCOUNT / key_blocker INVALID_ACCOUNT, team NONE, diagnostic_needed=false.
authentication.account_recovery_status == "FAILURE" OR authentication.last_login_status == "FAILURE" → status FAILED, route AUTH_FAILED / key_blocker AUTH_FAILED, team NONE, diagnostic_needed=false. (Auth gate beats status gate.)
status == "Suspended" → status FAILED, route INELIGIBLE_ACCOUNT, key_blocker OVERDUE_SUSPENSION (if overdue/hold — route team ACCOUNTS_PAYABLE) or FRAUD_SUSPENSION (if fraud). Infer the subtype from the customer report / queue note ("account hold", "overdue notice" → overdue; "fraud" → fraud). diagnostic_needed=false.
- Outage gate. Fetch
/api/outages?service_area=<ticket.service_area>. If any outage has active==true AND ticket.service_type ∈ outage.service_types → status PENDING_ACTION, route OUTAGE_WAIT, outage_id=<that outage_id>, team NONE, all issue flags false, diagnostic_needed=false. (tickets_requiring_customer_wait counts these.)
- Diagnostics. Otherwise fetch
/api/diagnostics/<ticket_id> and /api/troubleshooting/<ticket_id>. diagnostic_needed=true. Compute the three PRE flags (Section 2). Then:
- All three POST values clear floors → status
RESOLVED, route AUTO_TROUBLESHOOTING, team NONE.
- Any POST value still violates → status
ESCALATED, route ESCALATION, team = map from root_causes (Section 3). Set the matching key_blocker.
Output fields (batch template): ticket_id, account_id, final_resolution_status, diagnostic_needed, latency_issue, stability_issue, bandwidth_issue, outage_id, escalation_team, resolution_route + batch_summary{RESOLVED, PENDING_ACTION, ESCALATED, FAILED, tickets_requiring_customer_wait}. Preserve payload order. Empty outage_id = "". diagnostic_needed = whether the diagnostic step was part of the flow (true only for tickets that reached step 3).
Queue-QA variant (different field names): per-ticket final_resolution_status, route_team, key_blocker, diagnostic_required + queue_summary{FAILED, PENDING_ACTION, RESOLVED, ESCALATED, TIER2_SUPPORT, FIELD_OPS, NETWORK_ENGINEERING, ACCOUNTS_PAYABLE}. route_team = the escalation team (NONE for RESOLVED/outage/auth/invalid; ACCOUNTS_PAYABLE for overdue suspension; mapped team for ESCALATED). diagnostic_required == diagnostic_needed.
Family B — Mobile triage queue (case decision)
For each case fetch /api/cases/<case_id> → customer_id, line_id, device_id, issue_type, customer_location; then the line, device, plan, and (if billing) the customer's bill. Decision tree (primary → secondary → route):
- NO_SERVICE,
sim_status == "missing" (e.g. after a commute) → primary RESEAT_SIM, secondary TOGGLE_AIRPLANE_MODE (force network re-registration), route SELF_SERVICE.
- NO_SERVICE,
line.status == "Suspended" with suspension_reason == "OVERDUE_BILL" and customer ready to pay → primary SEND_PAYMENT_REQUEST, secondary RESUME_LINE_REBOOT; bill_id = the customer's Overdue bill, charge_amount_usd = that bill's amount_due_usd; route BILLING_RECOVERY.
- MOBILE_DATA,
customer_location == "abroad", phone_roaming_enabled == false, line.roaming_enabled == true → primary TOGGLE_ROAMING (enable phone-side roaming), route SELF_SERVICE. (Line already provisioned for roaming; only the device toggle is off.)
- MMS,
can_send_mms == false → primary GRANT_MESSAGING_PERMISSION; permission = whichever of sms/storage is false (sms_and_storage if both false); route SELF_SERVICE.
- SLOW_DATA,
vpn_connected == true → primary DISCONNECT_VPN; route SELF_SERVICE.
Output fields: case_id, customer_id, line_id, primary_action, secondary_action, permission, bill_id, charge_amount_usd, final_route + queue_summary{self_service_fixes, billing_recoveries, carrier_updates, human_transfers}. Preserve ascending case_id. permission ∈ NONE|sms|storage|sms_and_storage. bill_id empty string when not applicable; charge_amount_usd 0.00 otherwise.
Family C — Mobile-data recovery worklist
Per case fetch case → line → device → plan. Decision tree:
- Data stopped after usage limit:
line.data_used_gb > plan.data_limit_gb → primary REFUEL_DATA; data_refuel_gb = the customer's accepted refuel amount (from customer_preferences); charge_amount_usd = data_refuel_gb × plan.data_refueling_price_per_gb (2 decimals); route DATA_RECOVERY; carrier_update_required=false.
- Traveler roaming on phone but no data:
customer_location=="abroad", phone_roaming_enabled==true, line.roaming_enabled==false → primary ENABLE_LINE_ROAMING (carrier must provision line-side roaming); carrier_update_required=true; route CARRIER_UPDATE; data_refuel_gb=0.0, charge 0.00. (Mirror of Family-B abroad case: there phone toggle was off → TOGGLE_ROAMING/SELF_SERVICE; here the line side is off → carrier update.)
- Slow data, data-saver icon:
data_saver_mode==true → primary TOGGLE_DATA_SAVER; route DEVICE_SETTING_FIX.
- Slow data, older network mode:
network_mode_preference is 3g_only/non-4g5g → primary SET_NETWORK_MODE; route DEVICE_SETTING_FIX.
- No data after settings change:
mobile_data_enabled==false → primary TOGGLE_MOBILE_DATA; route DEVICE_SETTING_FIX.
secondary_action = NO_ACTION unless a follow-up re-connect step is clearly required. Output fields: case_id, primary_action, secondary_action, data_refuel_gb, charge_amount_usd, carrier_update_required, final_route + worklist_summary{data_refuel_cases, carrier_updates, device_setting_fixes, human_transfers, total_estimated_customer_charge_usd}. data_refuel_gb = 0.0 when not applicable (one decimal). total_estimated_customer_charge_usd = sum of all charge_amount_usd (typically only the refuel case; carrier/device fixes are free).
final_route enum: DATA_RECOVERY | CARRIER_UPDATE | DEVICE_SETTING_FIX | HUMAN_TRANSFER.
DATA_RECOVERY = data refuel.
CARRIER_UPDATE = action needs carrier-side provisioning (ENABLE_LINE_ROAMING when line roaming off; carrier_update_required=true).
DEVICE_SETTING_FIX = a device toggle/setting (mobile data, data saver, network mode, VPN, phone roaming when self-service).
HUMAN_TRANSFER = no automated fix applies (TRANSFER_HUMAN).
Family D — Enterprise export-complaint response package
- From the complaint email:
incident_id (e.g. INC-7301), client name, product. Match the client name to /api/enterprise/accounts → enterprise_account_id, account_owner, finance_owner. Confirm via /api/enterprise/incidents (the incident carries engineering_owner, account_owner, severity, status, product).
GET /api/enterprise/export-runs?incident_id=<id>. Build the failure_window:
start_date = first FAILED run run_date; end_date = last FAILED run run_date; failed_days = count of FAILED runs.
backfill_days = failed_days (the consecutive failed days requiring/undergoing manual backfill). The first SUCCEEDED run after the streak is the recovery point.
root_cause_category: infer from the export-run failure_code + the matching /api/enterprise/messages?query=... evidence (e.g. STALE_CREDENTIAL + "scheduler pod still references old secret" message; STAGING_STORAGE_QUOTA + "bucket reached quota" message). Concise category string.
contributing_alert_issue ∈ ARCHIVED_ALERT_ROUTE | NONE | UNKNOWN: set ARCHIVED_ALERT_ROUTE when a root-cause message was posted to a channel whose name contains archive (e.g. export-alerts-archive) — the alert was misrouted to a dead channel and slowed detection. Else NONE (or UNKNOWN if alert routing is unresolved).
sla_credit_percent: from /api/ enterprise/sla/<enterprise_account_id>.monthly_export_credit_percent (cross-check against the account-escalations message that states the contracted credit). Trigger text (e.g. "3 consecutive failed export runs" or "outage longer than 72 hours") is descriptive; the percent is the numeric field.
severity: copy from the incident record (Critical/High/Medium/Low).
engineering_owner, account_owner: copy from the incident record (also on the enterprise account).
- Constructed artifacts — follow the complaint's
naming_style requirement EXACTLY (these strings are scored on exact match):
channel_name: lowercase-hyphen channel name (per naming_style).
evidence_folder: "client-date" investigation folder, e.g. <client-lowercase-hyphen>-<YYYY-MM-DD>-investigation (date = incident/failure date).
report_title: " Export Failure Report" (per naming_style; include "Monthly" when the product is monthly_export).
share_permissions: one entry per user in the complaint's permission_users_to_include, ordered as listed. permission ∈ view|edit|upload_only, assigned by role (finance owner → edit on the financial/SLA section; designated collaborator → upload_only or edit; pure reviewers → view). The exact role→permission mapping is task-specific — read the requirements.
response_status ∈ READY_TO_SEND | NEEDS_FINANCE_REVIEW | NEEDS_ENGINEERING_REVIEW | UNDER_INVESTIGATION: set from what remains open. If an ARCHIVED_ALERT_ROUTE contributing issue exists (alerting still broken) or the engineering fix needs verification → NEEDS_ENGINEERING_REVIEW. If the SLA credit needs finance sign-off → NEEDS_FINANCE_REVIEW. If root cause is still unresolved → UNDER_INVESTIGATION. Only READY_TO_SEND when all of root cause, backfill, SLA, and owners are confirmed with no open review.
Pitfall (enterprise): the constructed strings (root_cause_category, channel_name, evidence_folder, report_title), the per-user permission values, and response_status are the hard part — they require exact matches. Always derive every deterministic field from records (incident, SLA, export-runs) and treat only the constructed strings as needing careful naming_style compliance.
5. Audit / Summary Formulas
- batch_summary.RESOLVED/PENDING_ACTION/ESCALATED/FAILED = counts of per-ticket
final_resolution_status.
- batch_summary.tickets_requiring_customer_wait = count of OUTAGE_WAIT tickets (active-outage gated).
- queue_summary status counts as above; team counts = counts of per-ticket
route_team (NONE entries are not emitted as a summary key).
- queue_summary (mobile triage):
self_service_fixes + billing_recoveries + carrier_updates + human_transfers = total cases; each case maps 1:1 to its final_route (SELF_SERVICE/BILLING_RECOVERY/CARRIER_UPDATE/HUMAN_TRANSFER).
- worklist_summary.total_estimated_customer_charge_usd = Σ
charge_amount_usd over all cases (round to 2 decimals). Only REFUEL_DATA cases normally carry a charge.
6. Pitfalls
- Always substitute the real base URL for any
127.0.0.1 address in a prompt.
- Gating before diagnostics. Account/auth/outage gates make a ticket FAILED or PENDING_ACTION with
diagnostic_needed=false and all issue flags false — do not compute floors for gated tickets.
- Outage must match service_type. An active outage in the service_area only gates the ticket if the ticket's
service_type is in outage.service_types.
- Auth gate beats status gate. A
status=Active account with account_recovery_status=FAILURE is AUTH_FAILED, not Active. Conversely, suspended accounts all have last_login_status=SUCCESS — the suspension is a status gate, not an auth gate.
- Flags are PRE, outcome is POST.
latency_issue/stability_issue/bandwidth_issue reflect the pre-troubleshoot snapshot; RESOLVED-vs-ESCALATED reflects the post-troubleshoot snapshot. A ticket can have all three pre-flags true yet still be RESOLVED (troubleshooting cleared them).
- Bandwidth is a ratio, not absolute. Compare
bandwidth_mbps / subscribed_mbps to 0.75 — subscribed_mbps varies per ticket (100/200/300/500/750).
- Mirror roaming cases. Abroad + phone roaming off (line on) →
TOGGLE_ROAMING (self-service). Abroad + line roaming off (phone on) → ENABLE_LINE_ROAMING (carrier update).
- REFUEL charge uses the LINE's plan price, not a flat rate:
plan.data_refueling_price_per_gb × refuel GB (e.g. PLAN-PREMIUM = $2.0/gb).
- Permission value: emit
NONE for non-MMS cases; for MMS emit the specific missing permission (sms/storage/sms_and_storage), not NONE.
- Preserve order: tickets/cases in payload order; cases ascending by
case_id when the template says so; share_permissions ordered as listed in requirements.
- Empty strings, not null:
outage_id="", bill_id="" when not applicable.
1---2name: reflect-3-attempt-01-103description: SCN_003 CRM support-console analyst. Resolve same-day ticket batches, mobile triage queues, mobile-data recovery worklists, and enterprise export-complaint response packages by querying the shared support-console API and applying fixed diagnostic thresholds, gating order, root-cause routing, and per-family decision trees.4---56# SCN_003 CRM Support Operations Analyst78Reusable rules for working a support-console workload across four task families. All evidence comes from the remote support-console API; never assume field values — fetch the records. Prompts may reference `127.0.0.1:8057`; always substitute the real base URL provided by the harness.910## 1. API Map1112Base URL: use the harness-provided base URL (e.g. `<remote-env-url>`). Health: `GET /health`.1314Public catalog: `GET /api/catalog` (endpoints + record counts only).1516- `GET /api/accounts` and `/api/accounts/<account_id>` — broadband/enterprise accounts. Fields: `account_id`, `name`, `service_area`, `status` (`Active`/`Suspended`), `tier`, `authentication{last_login_status, account_recovery_status, last_login_at}`. A missing account returns `{"error":"not_found"}`.17- `GET /api/tickets` and `/api/tickets/<ticket_id>` — tickets. Fields: `ticket_id`, `account_id`, `service_type` (`internet`/`video`/`voice`), `service_area`, `subscribed_mbps`, `status`, `issue_summary`.18- `GET /api/outages?service_area=<area>` — outages for an area. Each: `outage_id`, `service_area`, `active` (bool), `service_types` (array), `eta_hours`, `impact_score`, `started_at`.19- `GET /api/diagnostics/<ticket_id>` — pre-troubleshoot snapshot. Fields: `bandwidth_mbps`, `latency_ms`, `jitter_ms`, `root_causes` (array), `started_at`/`completed_at`.20- `GET /api/troubleshooting/<ticket_id>` — post-troubleshoot snapshot. Fields: `post_bandwidth_mbps`, `post_latency_ms`, `post_jitter_ms`, `steps` (array). (Not every ticket has diagnostics/troubleshooting records.)21- `GET /api/customers`, `/api/lines`, `/api/lines/<line_id>` — mobile. Line fields: `line_id`, `customer_id`, `device_id`, `plan_id`, `status`, `roaming_enabled`, `data_used_gb`, `suspension_reason` (e.g. `OVERDUE_BILL`).22- `GET /api/devices/<device_id>` — device state: `airplane_mode`, `mobile_data_enabled`, `phone_roaming_enabled`, `sim_status` (`active`/`missing`), `signal_strength`, `speed_test` (`excellent`/`good`/`fair`/`poor`/`no_connection`), `data_saver_mode`, `vpn_connected`, `network_mode_preference` (`4g_5g_preferred`/`3g_only`/…), `can_send_mms`, `mmsc_url_present`, `messaging_permissions{sms, storage}`, `wifi_calling_enabled`.23- `GET /api/plans` and `/api/plans/<plan_id>` — `data_limit_gb`, `data_refueling_price_per_gb`, `monthly_price_usd`.24- `GET /api/bills` — `bill_id`, `customer_id`, `amount_due_usd`, `due_date`, `status` (`Paid`/`Overdue`/`Issued`).25- `GET /api/cases` and `/api/cases/<case_id>` — mobile cases. Fields: `case_id`, `customer_id`, `line_id`, `device_id`, `issue_type`, `customer_location` (`home`/`abroad`), `summary`.26- `GET /api/enterprise/accounts` — `enterprise_account_id`, `name`, `tier`, `account_owner`, `finance_owner`.27- `GET /api/enterprise/incidents` — `incident_id`, `enterprise_account_id`, `product`, `severity`, `status`, `engineering_owner`, `account_owner`, `received_at`, `summary`.28- `GET /api/enterprise/export-runs?incident_id=<id>` — runs: `run_id`, `run_date`, `status` (`FAILED`/`SUCCEEDED`), `failure_code`, `exported_record_count`.29- `GET /api/enterprise/messages?query=<text>` — messages: `message_id`, `author`, `body`, `channel`, `created_at`.30- `GET /api/enterprise/sla/<enterprise_account_id>` — `monthly_export_credit_percent`, `credit_trigger`, `executive_contact`.3132## 2. Diagnostic Thresholds (EXACT, converged values)3334These floors decide per-ticket issue flags and the RESOLVED-vs-ESCALATED outcome. They are absolute for latency/jitter and a RATIO for bandwidth.3536| Metric | Floor | Violation test | Issue flag |37|---|---|---|---|38| Latency | **100 ms** (absolute) | `latency_ms > 100` | `latency_issue` |39| Jitter / stability | **30 ms** (absolute) | `jitter_ms > 30` | `stability_issue` |40| Bandwidth | **0.75 × subscribed_mbps** (ratio) | `bandwidth_mbps / subscribed_mbps < 0.75` | `bandwidth_issue` |4142- Flags are computed from the **PRE-troubleshoot** diagnostics snapshot.43- A ticket is **RESOLVED** by auto-troubleshooting **iff ALL three post-troubleshoot values clear the floors** (`post_latency_ms <= 100` AND `post_jitter_ms <= 30` AND `post_bandwidth_mbps / subscribed_mbps >= 0.75`).44- If any post value still violates → **ESCALATED** to the team mapped from the diagnostics `root_causes`.45- Boundary: a value exactly equal to the floor is treated as PASSING (strict `>` / `<` for violation).4647## 3. Root-Cause → Escalation Team Map4849Derived from `/api/diagnostics/<ticket_id>.root_causes`:5051| root_cause keyword | escalation_team / route_team | Notes |52|---|---|---|53| `FIBER_*`, `SIGNAL_LOSS`, `*DROP_DAMAGE`, physical line/fault | `FIELD_OPS` | key_blocker `PHYSICAL_LINE_FAULT` |54| `BACKBONE_*`, `*_CAPACITY` | `NETWORK_ENGINEERING` | key_blocker `NETWORK_CAPACITY` |55| `PROVISIONING_*` (stale/mismatch) | `TIER2_SUPPORT` | key_blocker `PROVISIONING_STALE` |56| Billing / overdue suspension | `ACCOUNTS_PAYABLE` | key_blocker `OVERDUE_SUSPENSION` |57| `CONFIGURATION_DRIFT`, `VOICE_PROFILE_STALE` | (cleared by auto-troubleshooting → `NONE`) | typically RESOLVED |5859## 4. Family SOPs6061### Family A — Ticket batch resolution (per-ticket decision)6263Gating order is strict; the FIRST gate that trips wins and **no diagnostics are run** for gated tickets.64651. **Account gate.** Fetch `/api/accounts/<account_id>`.66 - `not_found` (bad account id) → status `FAILED`, route `INELIGIBLE_ACCOUNT` / key_blocker `INVALID_ACCOUNT`, team `NONE`, `diagnostic_needed=false`.67 - `authentication.account_recovery_status == "FAILURE"` OR `authentication.last_login_status == "FAILURE"` → status `FAILED`, route `AUTH_FAILED` / key_blocker `AUTH_FAILED`, team `NONE`, `diagnostic_needed=false`. (Auth gate beats status gate.)68 - `status == "Suspended"` → status `FAILED`, route `INELIGIBLE_ACCOUNT`, key_blocker `OVERDUE_SUSPENSION` (if overdue/hold — route team `ACCOUNTS_PAYABLE`) or `FRAUD_SUSPENSION` (if fraud). Infer the subtype from the customer report / queue note ("account hold", "overdue notice" → overdue; "fraud" → fraud). `diagnostic_needed=false`.692. **Outage gate.** Fetch `/api/outages?service_area=<ticket.service_area>`. If any outage has `active==true` AND `ticket.service_type` ∈ `outage.service_types` → status `PENDING_ACTION`, route `OUTAGE_WAIT`, `outage_id=<that outage_id>`, team `NONE`, all issue flags `false`, `diagnostic_needed=false`. (`tickets_requiring_customer_wait` counts these.)703. **Diagnostics.** Otherwise fetch `/api/diagnostics/<ticket_id>` and `/api/troubleshooting/<ticket_id>`. `diagnostic_needed=true`. Compute the three PRE flags (Section 2). Then:71 - All three POST values clear floors → status `RESOLVED`, route `AUTO_TROUBLESHOOTING`, team `NONE`.72 - Any POST value still violates → status `ESCALATED`, route `ESCALATION`, team = map from `root_causes` (Section 3). Set the matching `key_blocker`.7374Output fields (batch template): `ticket_id, account_id, final_resolution_status, diagnostic_needed, latency_issue, stability_issue, bandwidth_issue, outage_id, escalation_team, resolution_route` + `batch_summary{RESOLVED, PENDING_ACTION, ESCALATED, FAILED, tickets_requiring_customer_wait}`. Preserve payload order. Empty `outage_id` = `""`. `diagnostic_needed` = whether the diagnostic step was part of the flow (true only for tickets that reached step 3).7576**Queue-QA variant** (different field names): per-ticket `final_resolution_status, route_team, key_blocker, diagnostic_required` + `queue_summary{FAILED, PENDING_ACTION, RESOLVED, ESCALATED, TIER2_SUPPORT, FIELD_OPS, NETWORK_ENGINEERING, ACCOUNTS_PAYABLE}`. `route_team` = the escalation team (NONE for RESOLVED/outage/auth/invalid; ACCOUNTS_PAYABLE for overdue suspension; mapped team for ESCALATED). `diagnostic_required` == `diagnostic_needed`.7778### Family B — Mobile triage queue (case decision)7980For each case fetch `/api/cases/<case_id>` → `customer_id, line_id, device_id, issue_type, customer_location`; then the line, device, plan, and (if billing) the customer's bill. Decision tree (primary → secondary → route):8182- **NO_SERVICE, `sim_status == "missing"`** (e.g. after a commute) → primary `RESEAT_SIM`, secondary `TOGGLE_AIRPLANE_MODE` (force network re-registration), route `SELF_SERVICE`.83- **NO_SERVICE, `line.status == "Suspended"` with `suspension_reason == "OVERDUE_BILL"`** and customer ready to pay → primary `SEND_PAYMENT_REQUEST`, secondary `RESUME_LINE_REBOOT`; `bill_id` = the customer's `Overdue` bill, `charge_amount_usd` = that bill's `amount_due_usd`; route `BILLING_RECOVERY`.84- **MOBILE_DATA, `customer_location == "abroad"`, `phone_roaming_enabled == false`, `line.roaming_enabled == true`** → primary `TOGGLE_ROAMING` (enable phone-side roaming), route `SELF_SERVICE`. (Line already provisioned for roaming; only the device toggle is off.)85- **MMS, `can_send_mms == false`** → primary `GRANT_MESSAGING_PERMISSION`; `permission` = whichever of `sms`/`storage` is `false` (`sms_and_storage` if both false); route `SELF_SERVICE`.86- **SLOW_DATA, `vpn_connected == true`** → primary `DISCONNECT_VPN`; route `SELF_SERVICE`.8788Output fields: `case_id, customer_id, line_id, primary_action, secondary_action, permission, bill_id, charge_amount_usd, final_route` + `queue_summary{self_service_fixes, billing_recoveries, carrier_updates, human_transfers}`. Preserve ascending `case_id`. `permission` ∈ `NONE|sms|storage|sms_and_storage`. `bill_id` empty string when not applicable; `charge_amount_usd` 0.00 otherwise.8990### Family C — Mobile-data recovery worklist9192Per case fetch case → line → device → plan. Decision tree:9394- **Data stopped after usage limit**: `line.data_used_gb > plan.data_limit_gb` → primary `REFUEL_DATA`; `data_refuel_gb` = the customer's accepted refuel amount (from `customer_preferences`); `charge_amount_usd` = `data_refuel_gb × plan.data_refueling_price_per_gb` (2 decimals); route `DATA_RECOVERY`; `carrier_update_required=false`.95- **Traveler roaming on phone but no data**: `customer_location=="abroad"`, `phone_roaming_enabled==true`, `line.roaming_enabled==false` → primary `ENABLE_LINE_ROAMING` (carrier must provision line-side roaming); `carrier_update_required=true`; route `CARRIER_UPDATE`; `data_refuel_gb=0.0`, charge `0.00`. (Mirror of Family-B abroad case: there phone toggle was off → `TOGGLE_ROAMING`/SELF_SERVICE; here the line side is off → carrier update.)96- **Slow data, data-saver icon**: `data_saver_mode==true` → primary `TOGGLE_DATA_SAVER`; route `DEVICE_SETTING_FIX`.97- **Slow data, older network mode**: `network_mode_preference` is `3g_only`/non-4g5g → primary `SET_NETWORK_MODE`; route `DEVICE_SETTING_FIX`.98- **No data after settings change**: `mobile_data_enabled==false` → primary `TOGGLE_MOBILE_DATA`; route `DEVICE_SETTING_FIX`.99100`secondary_action` = `NO_ACTION` unless a follow-up re-connect step is clearly required. Output fields: `case_id, primary_action, secondary_action, data_refuel_gb, charge_amount_usd, carrier_update_required, final_route` + `worklist_summary{data_refuel_cases, carrier_updates, device_setting_fixes, human_transfers, total_estimated_customer_charge_usd}`. `data_refuel_gb` = `0.0` when not applicable (one decimal). `total_estimated_customer_charge_usd` = sum of all `charge_amount_usd` (typically only the refuel case; carrier/device fixes are free).101102`final_route` enum: `DATA_RECOVERY | CARRIER_UPDATE | DEVICE_SETTING_FIX | HUMAN_TRANSFER`.103- `DATA_RECOVERY` = data refuel.104- `CARRIER_UPDATE` = action needs carrier-side provisioning (`ENABLE_LINE_ROAMING` when line roaming off; `carrier_update_required=true`).105- `DEVICE_SETTING_FIX` = a device toggle/setting (mobile data, data saver, network mode, VPN, phone roaming when self-service).106- `HUMAN_TRANSFER` = no automated fix applies (`TRANSFER_HUMAN`).107108### Family D — Enterprise export-complaint response package1091101. From the complaint email: `incident_id` (e.g. INC-7301), client name, product. Match the client name to `/api/enterprise/accounts` → `enterprise_account_id`, `account_owner`, `finance_owner`. Confirm via `/api/enterprise/incidents` (the incident carries `engineering_owner`, `account_owner`, `severity`, `status`, `product`).1112. `GET /api/enterprise/export-runs?incident_id=<id>`. Build the `failure_window`:112 - `start_date` = first FAILED run `run_date`; `end_date` = last FAILED run `run_date`; `failed_days` = count of FAILED runs.113 - `backfill_days` = `failed_days` (the consecutive failed days requiring/undergoing manual backfill). The first SUCCEEDED run after the streak is the recovery point.1143. `root_cause_category`: infer from the export-run `failure_code` + the matching `/api/enterprise/messages?query=...` evidence (e.g. `STALE_CREDENTIAL` + "scheduler pod still references old secret" message; `STAGING_STORAGE_QUOTA` + "bucket reached quota" message). Concise category string.1154. `contributing_alert_issue` ∈ `ARCHIVED_ALERT_ROUTE | NONE | UNKNOWN`: set `ARCHIVED_ALERT_ROUTE` when a root-cause message was posted to a channel whose name contains `archive` (e.g. `export-alerts-archive`) — the alert was misrouted to a dead channel and slowed detection. Else `NONE` (or `UNKNOWN` if alert routing is unresolved).1165. `sla_credit_percent`: from `/api/ enterprise/sla/<enterprise_account_id>.monthly_export_credit_percent` (cross-check against the account-escalations message that states the contracted credit). Trigger text (e.g. "3 consecutive failed export runs" or "outage longer than 72 hours") is descriptive; the percent is the numeric field.1176. `severity`: copy from the incident record (`Critical`/`High`/`Medium`/`Low`).1187. `engineering_owner`, `account_owner`: copy from the incident record (also on the enterprise account).1198. Constructed artifacts — follow the complaint's `naming_style` requirement EXACTLY (these strings are scored on exact match):120 - `channel_name`: lowercase-hyphen channel name (per `naming_style`).121 - `evidence_folder`: "client-date" investigation folder, e.g. `<client-lowercase-hyphen>-<YYYY-MM-DD>-investigation` (date = incident/failure date).122 - `report_title`: "<Client> Export Failure Report" (per `naming_style`; include "Monthly" when the product is `monthly_export`).1239. `share_permissions`: one entry per user in the complaint's `permission_users_to_include`, ordered as listed. `permission` ∈ `view|edit|upload_only`, assigned by role (finance owner → edit on the financial/SLA section; designated collaborator → upload_only or edit; pure reviewers → view). The exact role→permission mapping is task-specific — read the requirements.12410. `response_status` ∈ `READY_TO_SEND | NEEDS_FINANCE_REVIEW | NEEDS_ENGINEERING_REVIEW | UNDER_INVESTIGATION`: set from what remains open. If an `ARCHIVED_ALERT_ROUTE` contributing issue exists (alerting still broken) or the engineering fix needs verification → `NEEDS_ENGINEERING_REVIEW`. If the SLA credit needs finance sign-off → `NEEDS_FINANCE_REVIEW`. If root cause is still unresolved → `UNDER_INVESTIGATION`. Only `READY_TO_SEND` when all of root cause, backfill, SLA, and owners are confirmed with no open review.125126**Pitfall (enterprise):** the constructed strings (`root_cause_category`, `channel_name`, `evidence_folder`, `report_title`), the per-user `permission` values, and `response_status` are the hard part — they require exact matches. Always derive every deterministic field from records (incident, SLA, export-runs) and treat only the constructed strings as needing careful `naming_style` compliance.127128## 5. Audit / Summary Formulas129130- **batch_summary.RESOLVED/PENDING_ACTION/ESCALATED/FAILED** = counts of per-ticket `final_resolution_status`.131- **batch_summary.tickets_requiring_customer_wait** = count of OUTAGE_WAIT tickets (active-outage gated).132- **queue_summary** status counts as above; team counts = counts of per-ticket `route_team` (NONE entries are not emitted as a summary key).133- **queue_summary (mobile triage)**: `self_service_fixes` + `billing_recoveries` + `carrier_updates` + `human_transfers` = total cases; each case maps 1:1 to its `final_route` (SELF_SERVICE/BILLING_RECOVERY/CARRIER_UPDATE/HUMAN_TRANSFER).134- **worklist_summary.total_estimated_customer_charge_usd** = Σ `charge_amount_usd` over all cases (round to 2 decimals). Only REFUEL_DATA cases normally carry a charge.135136## 6. Pitfalls137138- **Always substitute the real base URL** for any `127.0.0.1` address in a prompt.139- **Gating before diagnostics.** Account/auth/outage gates make a ticket FAILED or PENDING_ACTION with `diagnostic_needed=false` and all issue flags false — do not compute floors for gated tickets.140- **Outage must match service_type.** An active outage in the service_area only gates the ticket if the ticket's `service_type` is in `outage.service_types`.141- **Auth gate beats status gate.** A `status=Active` account with `account_recovery_status=FAILURE` is AUTH_FAILED, not Active. Conversely, suspended accounts all have `last_login_status=SUCCESS` — the suspension is a status gate, not an auth gate.142- **Flags are PRE, outcome is POST.** `latency_issue/stability_issue/bandwidth_issue` reflect the pre-troubleshoot snapshot; RESOLVED-vs-ESCALATED reflects the post-troubleshoot snapshot. A ticket can have all three pre-flags true yet still be RESOLVED (troubleshooting cleared them).143- **Bandwidth is a ratio, not absolute.** Compare `bandwidth_mbps / subscribed_mbps` to 0.75 — `subscribed_mbps` varies per ticket (100/200/300/500/750).144- **Mirror roaming cases.** Abroad + phone roaming off (line on) → `TOGGLE_ROAMING` (self-service). Abroad + line roaming off (phone on) → `ENABLE_LINE_ROAMING` (carrier update).145- **REFUEL charge uses the LINE's plan price**, not a flat rate: `plan.data_refueling_price_per_gb` × refuel GB (e.g. PLAN-PREMIUM = $2.0/gb).146- **Permission value**: emit `NONE` for non-MMS cases; for MMS emit the specific missing permission (`sms`/`storage`/`sms_and_storage`), not `NONE`.147- **Preserve order**: tickets/cases in payload order; cases ascending by `case_id` when the template says so; `share_permissions` ordered as listed in requirements.148- **Empty strings, not null**: `outage_id=""`, `bill_id=""` when not applicable.