Support-Console Resolution Skill
When to use
Use this skill for any task that hands you a small batch/queue of support records
(tickets, mobile cases, or an enterprise complaint) and asks you to classify or
resolve each item and emit a strict JSON answer. The work is always: (1) read the
payload list, (2) look up authoritative records from the shared support-console REST
API, (3) apply deterministic business rules, (4) emit ONLY JSON matching the task's
answer_template.json.
Never guess record contents from the customer's prose — the prose only hints at the
issue type. The decision is driven by the actual API record fields.
API base URL and lookup habits
Base URL: <remote-env-url> (this overrides any 127.0.0.1:8057
shown in a prompt). All endpoints are GET and read-only.
Start with /api/catalog if unsure what exists. Core endpoints and how to chain them:
- Tickets family (CSV of
ticket_id, account_id, service_type, ...):
GET /api/tickets/<ticket_id> -> service_area, service_type, subscribed_mbps.
GET /api/accounts/<account_id> -> status (Active/Suspended), authentication block.
A missing account returns {"error":"not_found"}.
GET /api/diagnostics/<ticket_id> -> pre-fix latency_ms, jitter_ms, bandwidth_mbps, root_causes[].
GET /api/troubleshooting/<ticket_id> -> steps[] and post_* metrics (after auto-fix).
GET /api/outages?service_area=<area> -> list (may be empty []); each has
active, service_types[], outage_id, eta_hours.
- Mobile cases family (case list of
case_id):
GET /api/cases (or /api/cases/<id>) -> customer_id, line_id, device_id,
issue_type, customer_location.
GET /api/lines/<line_id> -> status, suspension_reason, roaming_enabled,
plan_id, data_used_gb.
GET /api/devices/<device_id> -> the device-state flags (see vocab below).
GET /api/plans/<plan_id> -> data_limit_gb, data_refueling_price_per_gb.
GET /api/bills -> filter by customer_id for status/amount_due_usd/bill_id.
GET /api/customers -> customer status/name (returns the full list; filter locally).
- Enterprise family (incident reference in the complaint):
GET /api/enterprise/incidents/<incident_id> -> enterprise_account_id, severity,
product, engineering_owner, account_owner, status.
GET /api/enterprise/accounts -> finance_owner, account_owner, tier, name.
GET /api/enterprise/export-runs?incident_id=<id> -> per-day runs with status,
failure_code, run_date.
GET /api/enterprise/sla/<enterprise_account_id> -> monthly_export_credit_percent,
credit_trigger.
GET /api/enterprise/messages?query=<text> -> internal chatter; search by client
name and by "alert"/"archive" to find contributing-alert evidence.
Always preserve the ID order required by the template (payload order for tickets;
ascending case_id for cases). Compute every summary count by tallying your own
per-item decisions — never eyeball it.
SOP A — Offline ticket batch resolution
(template has per-ticket: final_resolution_status, diagnostic_needed/diagnostic_required,
issue flags, outage_id, escalation_team/route_team, resolution_route/key_blocker)
Evaluate each ticket with this precedence (first matching rule wins for status/route):
- Account invalid — account lookup returns not_found ->
status FAILED; route/team NONE; route INVALID_ACCOUNT; key_blocker INVALID_ACCOUNT;
no diagnostic needed.
- Account Suspended (
status: "Suspended") ->
- If suspension is for an overdue bill / general hold: status FAILED; route/team
NONE; route INELIGIBLE_ACCOUNT; key_blocker OVERDUE_SUSPENSION; no diagnostic.
- Authentication failed (account.authentication.last_login_status == "FAILURE"
or account_recovery_status == "FAILURE", report "authentication never recovered") ->
status ESCALATED; route/team TIER2_SUPPORT; route ESCALATION; key_blocker AUTH_FAILED;
no diagnostic.
- Active outage covering this service_type in the ticket's service_area
(
/api/outages?service_area= returns an entry with active:true whose
service_types includes the ticket's service_type) ->
status PENDING_ACTION; route/team NONE; resolution_route OUTAGE_WAIT;
key_blocker ACTIVE_OUTAGE; set outage_id to that outage; this ticket counts as a
"customer wait"; no diagnostic.
- Otherwise run the diagnostic path (account Active, no outage):
- A real diagnostic drives the decision -> diagnostic_needed/required = true.
- Inspect
root_causes[]:
- Physical-plant causes (FIBER_DROP_DAMAGE, SIGNAL_LOSS) ->
status ESCALATED; team FIELD_OPS; route ESCALATION; key_blocker
PHYSICAL_LINE_FAULT.
- Backbone/network-capacity cause (BACKBONE_CAPACITY) ->
status ESCALATED; team NETWORK_ENGINEERING; route ESCALATION; key_blocker
NETWORK_CAPACITY.
- Provisioning cause (PROVISIONING_STALE / CONFIGURATION_DRIFT) and a stale-profile
fix step ran: judge by recovery (rule 6). CONFIGURATION_DRIFT that heals ->
RESOLVED. PROVISIONING_STALE that does NOT fully heal -> ESCALATED to
TIER2_SUPPORT, key_blocker PROVISIONING_STALE (do NOT downgrade to
PENDING_ACTION/NONE — keep the team and the blocker).
- Voice profile cause (VOICE_PROFILE_STALE) with VOICE_PROFILE_REFRESH that heals
-> RESOLVED, team/route NONE, key_blocker NONE.
- Auto-resolution test (when a real troubleshooting fix ran):
Compare
post_* metrics to health. The system is HEALED when post_latency_ms drops
back to roughly < 100 ms (and jitter normalizes). If healed -> status RESOLVED,
team/route NONE, key_blocker NONE. If post_latency_ms is still elevated (>~100 ms,
e.g. only nudged 188->176 or 136->121) -> NOT resolved: escalate per the root-cause
team above, and keep the matching key_blocker. Bandwidth that is merely below the
subscribed rate does NOT by itself block RESOLVED (voice cases recover even with
bandwidth under subscription); latency recovery is the primary signal.
Decoy / noise diagnostics
A root_causes of GENERATED_NOISE paired with a GENERATED_CHECK troubleshooting
step is a filler/no-op diagnostic. It is NOT the real blocker. When such a ticket also
has a real upstream blocker (outage / invalid / auth / suspension), classify by the
upstream blocker and set diagnostic_needed/required = FALSE. Do not let GENERATED_NOISE
metrics fool you into AUTO_TROUBLESHOOTING.
Issue flags (when the template asks for latency/stability/bandwidth booleans)
Derive these from the pre-fix diagnostics record, independent of the final status:
latency_issue = diagnostics.latency_ms is high (roughly >= 100 ms).
stability_issue = diagnostics.jitter_ms is high (roughly >= 30 ms).
bandwidth_issue = diagnostics.bandwidth_mbps < ticket.subscribed_mbps.
(Pitfall: set bandwidth_issue TRUE whenever measured bandwidth is below the
subscribed rate even for an escalated/physical-fault ticket — do not zero the flags
just because the ticket is escalated.)
For tickets short-circuited by outage/invalid/auth/suspension, the issue flags are all
false (no meaningful diagnostic was the basis of the decision) and outage_id is "" unless
rule 4 applied.
Enum vocabularies (tickets)
- final_resolution_status: RESOLVED | PENDING_ACTION | ESCALATED | FAILED
- escalation_team / route_team: NONE | TIER2_SUPPORT | FIELD_OPS | NETWORK_ENGINEERING |
ACCOUNTS_PAYABLE
- resolution_route: AUTO_TROUBLESHOOTING | OUTAGE_WAIT | ESCALATION | INELIGIBLE_ACCOUNT |
AUTH_FAILED | INVALID_ACCOUNT
- key_blocker: NONE | ACTIVE_OUTAGE | INVALID_ACCOUNT | AUTH_FAILED | OVERDUE_SUSPENSION |
FRAUD_SUSPENSION | NETWORK_CAPACITY | PROVISIONING_STALE | PHYSICAL_LINE_FAULT
- outage_id: the matching OUT-id, else "" (empty string, never null).
batch/queue summary: count each status, count each route_team used, and (tickets template)
tickets_requiring_customer_wait = number of OUTAGE_WAIT tickets.
SOP B — Contact-center / mobile case triage
(template fields: primary_action, secondary_action, permission, bill_id,
charge_amount_usd, final_route — and the data-recovery variant: data_refuel_gb,
charge_amount_usd, carrier_update_required, final_route)
For each case, pull line + device (+ bill/plan when relevant), then map by the device
state, not the prose. Decision rules:
- Line Suspended for overdue bill (line.status Suspended, suspension_reason
OVERDUE_BILL; bill Overdue) -> primary SEND_PAYMENT_REQUEST, secondary
RESUME_LINE_REBOOT, bill_id = that bill, charge_amount_usd = bill.amount_due_usd,
final_route BILLING_RECOVERY.
- SIM missing / no service (device.sim_status == "missing", signal "none") ->
primary RESEAT_SIM, final_route SELF_SERVICE.
- MMS cannot send (device.can_send_mms false): the blocker is a missing messaging
permission. If storage permission is false -> GRANT_MESSAGING_PERMISSION with
permission "storage" (use "sms", "storage", or "sms_and_storage" to match whichever
permission(s) are false; "NONE" when none). final_route SELF_SERVICE.
- Slow data with VPN connected (device.vpn_connected true) -> DISCONNECT_VPN,
SELF_SERVICE.
- Roaming abroad, no data:
- line.roaming_enabled false (carrier side off) -> ENABLE_LINE_ROAMING,
carrier_update_required true, final_route CARRIER_UPDATE.
- line roaming on but device phone_roaming_enabled false -> TOGGLE_ROAMING
(device toggle), final_route SELF_SERVICE / DEVICE_SETTING_FIX.
- Data stopped after usage limit (line.data_used_gb >= plan.data_limit_gb) ->
REFUEL_DATA. Refuel amount = customer's accepted_refuel_gb (from preferences) when
given, else the gap needed. charge = refuel_gb * plan.data_refueling_price_per_gb,
two decimals. final_route DATA_RECOVERY. Respect
does_not_want_plan_change.
- Slow data, data-saver icon (device.data_saver_mode true) -> TOGGLE_DATA_SAVER,
DEVICE_SETTING_FIX.
- Slow data on old network mode (device.network_mode_preference "3g_only") ->
SET_NETWORK_MODE, DEVICE_SETTING_FIX.
- No data after settings change (device.mobile_data_enabled false) ->
TOGGLE_MOBILE_DATA, DEVICE_SETTING_FIX.
- Nothing actionable / needs a person -> TRANSFER_HUMAN, HUMAN_TRANSFER.
Use NO_ACTION for secondary_action whenever no follow-up is needed.
Only REFUEL_DATA (and billing payment) produce a non-zero charge; everything else is
charge 0.00. Permissions field is NONE unless rule 3 applies. bill_id is "" unless a
bill is actually involved.
Summary computation (mobile)
- self_service_fixes / device_setting_fixes = count of SELF_SERVICE / DEVICE_SETTING_FIX
routes.
- billing_recoveries = BILLING_RECOVERY count; carrier_updates = CARRIER_UPDATE count;
human_transfers = HUMAN_TRANSFER count; data_refuel_cases = DATA_RECOVERY count.
- total_estimated_customer_charge_usd = sum of every charge_amount_usd, two decimals.
Enum vocabularies (mobile)
- actions (superset): TOGGLE_AIRPLANE_MODE, RESEAT_SIM, RESET_APN_REBOOT,
SEND_PAYMENT_REQUEST, RESUME_LINE_REBOOT, TRANSFER_HUMAN, TOGGLE_MOBILE_DATA,
TOGGLE_ROAMING, ENABLE_LINE_ROAMING, REFUEL_DATA, TOGGLE_DATA_SAVER, SET_NETWORK_MODE,
DISCONNECT_VPN, GRANT_MESSAGING_PERMISSION, TOGGLE_WIFI_CALLING, NO_ACTION.
(The data-recovery template exposes only the data-relevant subset — use only the enum
values that template lists.)
- permission: NONE | sms | storage | sms_and_storage
- final_route (contact center): SELF_SERVICE | BILLING_RECOVERY | CARRIER_UPDATE |
HUMAN_TRANSFER
- final_route (data recovery): DATA_RECOVERY | CARRIER_UPDATE | DEVICE_SETTING_FIX |
HUMAN_TRANSFER
SOP C — Enterprise export-complaint response package
(template: incident_id, enterprise_account_id, root_cause_category,
contributing_alert_issue, failure_window{start_date,end_date,failed_days}, backfill_days,
sla_credit_percent, severity, engineering_owner, account_owner, channel_name,
evidence_folder, report_title, share_permissions[], response_status)
Procedure:
- Take the incident reference from the complaint email; confirm via
/api/enterprise/incidents/<id> -> enterprise_account_id, severity, product,
engineering_owner, account_owner.
/api/enterprise/export-runs?incident_id=<id> -> identify the consecutive FAILED
runs. failure_window.start_date = first FAILED run_date, end_date = last FAILED
run_date, failed_days = count of FAILED runs. The later SUCCEEDED run is the recovery,
not a failure. backfill_days = number of failed days that must be manually
re-run (= failed_days).
- root_cause_category = the export-run
failure_code corroborated by message evidence
(e.g. a credential rotation that left a stale secret => stale credential).
- contributing_alert_issue: search
/api/enterprise/messages by client name and
"alert"/"archive". If the alerting message landed in an archived alert channel
(channel name containing "archive") so the team missed it ->
ARCHIVED_ALERT_ROUTE. Else NONE (use UNKNOWN only if truly indeterminate).
- sla_credit_percent =
/api/enterprise/sla/<enterprise_account_id>
monthly_export_credit_percent (an integer percent). Confirm the credit_trigger
(e.g. "3 consecutive failed runs") is met.
- severity = the incident's severity enum (Critical | High | Medium | Low).
- engineering_owner / account_owner = incident's owner user ids. finance_owner comes
from the enterprise account record (used for share permissions / finance review).
- channel_name / evidence_folder / report_title: build from the response_requirements
naming_style exactly (lowercase-hyphen channel; client+date investigation folder;
" Export Failure Report" style title). Match the requested casing/format
token-for-token; these are exact-string-scored and easy to lose points on.
- share_permissions: include exactly the users in
permission_users_to_include, in the
listed order, each with a permission from {view, edit, upload_only}.
- response_status: if a financial SLA credit must be approved before sending ->
NEEDS_FINANCE_REVIEW; if engineering sign-off is still pending ->
NEEDS_ENGINEERING_REVIEW; if the incident is still being investigated ->
UNDER_INVESTIGATION; only READY_TO_SEND when nothing is outstanding.
Reliable fields here: incident_id, enterprise_account_id, owners, the failure window
dates+count, sla_credit_percent, severity, and contributing_alert_issue. The free-text
artifact strings (channel_name, evidence_folder, report_title) and share-permission
levels are exact-match and the biggest risk — derive them strictly from the provided
naming_style and user list rather than inventing your own convention.
Output-format discipline (all task families)
- Return ONLY the JSON object conforming to the task's
answer_template.json. No prose,
no markdown fences, no extra keys.
- Preserve the exact ID ordering the template demands.
- Enums must match the template spelling EXACTLY (uppercase enum tokens, exact casing of
severity/permission). A near-synonym scores as wrong.
- Numbers: money to two decimals, data_refuel_gb to one decimal, integers for counts and
percents. Use 0.0 / 0.00 (not null) when "not applicable"; use "" (empty string, not
null) for not-applicable string/ID fields.
- Recompute all summary counts from your own per-item decisions so they are internally
consistent.
- Booleans are real JSON booleans, not strings.
Pitfalls learned (reusable)
- bandwidth_issue depends only on measured-vs-subscribed; it stays TRUE on escalated
physical-fault tickets too. Do not blank issue flags just because a ticket escalates.
- A PROVISIONING_STALE ticket whose auto-fix leaves latency still elevated escalates to
TIER2_SUPPORT and KEEPS the PROVISIONING_STALE blocker — it is not PENDING_ACTION and
not NONE. Auto-resolution requires genuine metric recovery (latency back under ~100ms).
- GENERATED_NOISE root cause + GENERATED_CHECK step = decoy; the real classification comes
from the upstream blocker, and diagnostic_required is false for those tickets.
- For enterprise packages, the deterministic API-derived fields are dependable; sink your
effort into matching the requested naming conventions and permission levels exactly,
since those exact-string fields are where points silently leak.
1---2name: reflect-3-attempt-02-263description: Support-Console Resolution Skill4---5# Support-Console Resolution Skill67## When to use8Use this skill for any task that hands you a small batch/queue of support records9(tickets, mobile cases, or an enterprise complaint) and asks you to classify or10resolve each item and emit a strict JSON answer. The work is always: (1) read the11payload list, (2) look up authoritative records from the shared support-console REST12API, (3) apply deterministic business rules, (4) emit ONLY JSON matching the task's13`answer_template.json`.1415Never guess record contents from the customer's prose — the prose only hints at the16issue *type*. The decision is driven by the actual API record fields.1718## API base URL and lookup habits19Base URL: `<remote-env-url>` (this overrides any `127.0.0.1:8057`20shown in a prompt). All endpoints are GET and read-only.2122Start with `/api/catalog` if unsure what exists. Core endpoints and how to chain them:2324- Tickets family (CSV of `ticket_id, account_id, service_type, ...`):25 - `GET /api/tickets/<ticket_id>` -> service_area, service_type, subscribed_mbps.26 - `GET /api/accounts/<account_id>` -> status (Active/Suspended), authentication block.27 A missing account returns `{"error":"not_found"}`.28 - `GET /api/diagnostics/<ticket_id>` -> pre-fix latency_ms, jitter_ms, bandwidth_mbps, root_causes[].29 - `GET /api/troubleshooting/<ticket_id>` -> steps[] and post_* metrics (after auto-fix).30 - `GET /api/outages?service_area=<area>` -> list (may be empty `[]`); each has31 active, service_types[], outage_id, eta_hours.32- Mobile cases family (case list of `case_id`):33 - `GET /api/cases` (or `/api/cases/<id>`) -> customer_id, line_id, device_id,34 issue_type, customer_location.35 - `GET /api/lines/<line_id>` -> status, suspension_reason, roaming_enabled,36 plan_id, data_used_gb.37 - `GET /api/devices/<device_id>` -> the device-state flags (see vocab below).38 - `GET /api/plans/<plan_id>` -> data_limit_gb, data_refueling_price_per_gb.39 - `GET /api/bills` -> filter by customer_id for status/amount_due_usd/bill_id.40 - `GET /api/customers` -> customer status/name (returns the full list; filter locally).41- Enterprise family (incident reference in the complaint):42 - `GET /api/enterprise/incidents/<incident_id>` -> enterprise_account_id, severity,43 product, engineering_owner, account_owner, status.44 - `GET /api/enterprise/accounts` -> finance_owner, account_owner, tier, name.45 - `GET /api/enterprise/export-runs?incident_id=<id>` -> per-day runs with status,46 failure_code, run_date.47 - `GET /api/enterprise/sla/<enterprise_account_id>` -> monthly_export_credit_percent,48 credit_trigger.49 - `GET /api/enterprise/messages?query=<text>` -> internal chatter; search by client50 name and by "alert"/"archive" to find contributing-alert evidence.5152Always preserve the ID order required by the template (payload order for tickets;53ascending case_id for cases). Compute every summary count by tallying your own54per-item decisions — never eyeball it.5556---5758## SOP A — Offline ticket batch resolution59(template has per-ticket: final_resolution_status, diagnostic_needed/diagnostic_required,60issue flags, outage_id, escalation_team/route_team, resolution_route/key_blocker)6162Evaluate each ticket with this precedence (first matching rule wins for status/route):63641. **Account invalid** — account lookup returns not_found ->65 status FAILED; route/team NONE; route INVALID_ACCOUNT; key_blocker INVALID_ACCOUNT;66 no diagnostic needed.672. **Account Suspended** (`status: "Suspended"`) ->68 - If suspension is for an overdue bill / general hold: status FAILED; route/team69 NONE; route INELIGIBLE_ACCOUNT; key_blocker OVERDUE_SUSPENSION; no diagnostic.703. **Authentication failed** (account.authentication.last_login_status == "FAILURE"71 or account_recovery_status == "FAILURE", report "authentication never recovered") ->72 status ESCALATED; route/team TIER2_SUPPORT; route ESCALATION; key_blocker AUTH_FAILED;73 no diagnostic.744. **Active outage** covering this service_type in the ticket's service_area75 (`/api/outages?service_area=` returns an entry with `active:true` whose76 `service_types` includes the ticket's service_type) ->77 status PENDING_ACTION; route/team NONE; resolution_route OUTAGE_WAIT;78 key_blocker ACTIVE_OUTAGE; set outage_id to that outage; this ticket counts as a79 "customer wait"; no diagnostic.805. **Otherwise run the diagnostic path** (account Active, no outage):81 - A real diagnostic drives the decision -> diagnostic_needed/required = true.82 - Inspect `root_causes[]`:83 - Physical-plant causes (FIBER_DROP_DAMAGE, SIGNAL_LOSS) ->84 status ESCALATED; team FIELD_OPS; route ESCALATION; key_blocker85 PHYSICAL_LINE_FAULT.86 - Backbone/network-capacity cause (BACKBONE_CAPACITY) ->87 status ESCALATED; team NETWORK_ENGINEERING; route ESCALATION; key_blocker88 NETWORK_CAPACITY.89 - Provisioning cause (PROVISIONING_STALE / CONFIGURATION_DRIFT) and a stale-profile90 fix step ran: judge by recovery (rule 6). CONFIGURATION_DRIFT that heals ->91 RESOLVED. PROVISIONING_STALE that does NOT fully heal -> ESCALATED to92 TIER2_SUPPORT, key_blocker PROVISIONING_STALE (do NOT downgrade to93 PENDING_ACTION/NONE — keep the team and the blocker).94 - Voice profile cause (VOICE_PROFILE_STALE) with VOICE_PROFILE_REFRESH that heals95 -> RESOLVED, team/route NONE, key_blocker NONE.966. **Auto-resolution test** (when a real troubleshooting fix ran):97 Compare `post_*` metrics to health. The system is HEALED when post_latency_ms drops98 back to roughly < 100 ms (and jitter normalizes). If healed -> status RESOLVED,99 team/route NONE, key_blocker NONE. If post_latency_ms is still elevated (>~100 ms,100 e.g. only nudged 188->176 or 136->121) -> NOT resolved: escalate per the root-cause101 team above, and keep the matching key_blocker. Bandwidth that is merely below the102 subscribed rate does NOT by itself block RESOLVED (voice cases recover even with103 bandwidth under subscription); latency recovery is the primary signal.104105### Decoy / noise diagnostics106A `root_causes` of `GENERATED_NOISE` paired with a `GENERATED_CHECK` troubleshooting107step is a filler/no-op diagnostic. It is NOT the real blocker. When such a ticket also108has a real upstream blocker (outage / invalid / auth / suspension), classify by the109upstream blocker and set diagnostic_needed/required = FALSE. Do not let GENERATED_NOISE110metrics fool you into AUTO_TROUBLESHOOTING.111112### Issue flags (when the template asks for latency/stability/bandwidth booleans)113Derive these from the **pre-fix diagnostics** record, independent of the final status:114- `latency_issue` = diagnostics.latency_ms is high (roughly >= 100 ms).115- `stability_issue` = diagnostics.jitter_ms is high (roughly >= 30 ms).116- `bandwidth_issue` = diagnostics.bandwidth_mbps < ticket.subscribed_mbps.117 (Pitfall: set bandwidth_issue TRUE whenever measured bandwidth is below the118 subscribed rate even for an escalated/physical-fault ticket — do not zero the flags119 just because the ticket is escalated.)120For tickets short-circuited by outage/invalid/auth/suspension, the issue flags are all121false (no meaningful diagnostic was the basis of the decision) and outage_id is "" unless122rule 4 applied.123124### Enum vocabularies (tickets)125- final_resolution_status: RESOLVED | PENDING_ACTION | ESCALATED | FAILED126- escalation_team / route_team: NONE | TIER2_SUPPORT | FIELD_OPS | NETWORK_ENGINEERING |127 ACCOUNTS_PAYABLE128- resolution_route: AUTO_TROUBLESHOOTING | OUTAGE_WAIT | ESCALATION | INELIGIBLE_ACCOUNT |129 AUTH_FAILED | INVALID_ACCOUNT130- key_blocker: NONE | ACTIVE_OUTAGE | INVALID_ACCOUNT | AUTH_FAILED | OVERDUE_SUSPENSION |131 FRAUD_SUSPENSION | NETWORK_CAPACITY | PROVISIONING_STALE | PHYSICAL_LINE_FAULT132- outage_id: the matching OUT-id, else "" (empty string, never null).133134batch/queue summary: count each status, count each route_team used, and (tickets template)135`tickets_requiring_customer_wait` = number of OUTAGE_WAIT tickets.136137---138139## SOP B — Contact-center / mobile case triage140(template fields: primary_action, secondary_action, permission, bill_id,141charge_amount_usd, final_route — and the data-recovery variant: data_refuel_gb,142charge_amount_usd, carrier_update_required, final_route)143144For each case, pull line + device (+ bill/plan when relevant), then map by the device145state, not the prose. Decision rules:1461471. **Line Suspended for overdue bill** (line.status Suspended, suspension_reason148 OVERDUE_BILL; bill Overdue) -> primary SEND_PAYMENT_REQUEST, secondary149 RESUME_LINE_REBOOT, bill_id = that bill, charge_amount_usd = bill.amount_due_usd,150 final_route BILLING_RECOVERY.1512. **SIM missing / no service** (device.sim_status == "missing", signal "none") ->152 primary RESEAT_SIM, final_route SELF_SERVICE.1533. **MMS cannot send** (device.can_send_mms false): the blocker is a missing messaging154 permission. If storage permission is false -> GRANT_MESSAGING_PERMISSION with155 permission "storage" (use "sms", "storage", or "sms_and_storage" to match whichever156 permission(s) are false; "NONE" when none). final_route SELF_SERVICE.1574. **Slow data with VPN connected** (device.vpn_connected true) -> DISCONNECT_VPN,158 SELF_SERVICE.1595. **Roaming abroad, no data**:160 - line.roaming_enabled false (carrier side off) -> ENABLE_LINE_ROAMING,161 carrier_update_required true, final_route CARRIER_UPDATE.162 - line roaming on but device phone_roaming_enabled false -> TOGGLE_ROAMING163 (device toggle), final_route SELF_SERVICE / DEVICE_SETTING_FIX.1646. **Data stopped after usage limit** (line.data_used_gb >= plan.data_limit_gb) ->165 REFUEL_DATA. Refuel amount = customer's accepted_refuel_gb (from preferences) when166 given, else the gap needed. charge = refuel_gb * plan.data_refueling_price_per_gb,167 two decimals. final_route DATA_RECOVERY. Respect `does_not_want_plan_change`.1687. **Slow data, data-saver icon** (device.data_saver_mode true) -> TOGGLE_DATA_SAVER,169 DEVICE_SETTING_FIX.1708. **Slow data on old network mode** (device.network_mode_preference "3g_only") ->171 SET_NETWORK_MODE, DEVICE_SETTING_FIX.1729. **No data after settings change** (device.mobile_data_enabled false) ->173 TOGGLE_MOBILE_DATA, DEVICE_SETTING_FIX.17410. Nothing actionable / needs a person -> TRANSFER_HUMAN, HUMAN_TRANSFER.175176Use NO_ACTION for secondary_action whenever no follow-up is needed.177Only REFUEL_DATA (and billing payment) produce a non-zero charge; everything else is178charge 0.00. Permissions field is NONE unless rule 3 applies. bill_id is "" unless a179bill is actually involved.180181### Summary computation (mobile)182- self_service_fixes / device_setting_fixes = count of SELF_SERVICE / DEVICE_SETTING_FIX183 routes.184- billing_recoveries = BILLING_RECOVERY count; carrier_updates = CARRIER_UPDATE count;185 human_transfers = HUMAN_TRANSFER count; data_refuel_cases = DATA_RECOVERY count.186- total_estimated_customer_charge_usd = sum of every charge_amount_usd, two decimals.187188### Enum vocabularies (mobile)189- actions (superset): TOGGLE_AIRPLANE_MODE, RESEAT_SIM, RESET_APN_REBOOT,190 SEND_PAYMENT_REQUEST, RESUME_LINE_REBOOT, TRANSFER_HUMAN, TOGGLE_MOBILE_DATA,191 TOGGLE_ROAMING, ENABLE_LINE_ROAMING, REFUEL_DATA, TOGGLE_DATA_SAVER, SET_NETWORK_MODE,192 DISCONNECT_VPN, GRANT_MESSAGING_PERMISSION, TOGGLE_WIFI_CALLING, NO_ACTION.193 (The data-recovery template exposes only the data-relevant subset — use only the enum194 values that template lists.)195- permission: NONE | sms | storage | sms_and_storage196- final_route (contact center): SELF_SERVICE | BILLING_RECOVERY | CARRIER_UPDATE |197 HUMAN_TRANSFER198- final_route (data recovery): DATA_RECOVERY | CARRIER_UPDATE | DEVICE_SETTING_FIX |199 HUMAN_TRANSFER200201---202203## SOP C — Enterprise export-complaint response package204(template: incident_id, enterprise_account_id, root_cause_category,205contributing_alert_issue, failure_window{start_date,end_date,failed_days}, backfill_days,206sla_credit_percent, severity, engineering_owner, account_owner, channel_name,207evidence_folder, report_title, share_permissions[], response_status)208209Procedure:2101. Take the incident reference from the complaint email; confirm via211 `/api/enterprise/incidents/<id>` -> enterprise_account_id, severity, product,212 engineering_owner, account_owner.2132. `/api/enterprise/export-runs?incident_id=<id>` -> identify the consecutive FAILED214 runs. failure_window.start_date = first FAILED run_date, end_date = last FAILED215 run_date, failed_days = count of FAILED runs. The later SUCCEEDED run is the recovery,216 not a failure. backfill_days = number of failed days that must be manually217 re-run (= failed_days).2183. root_cause_category = the export-run `failure_code` corroborated by message evidence219 (e.g. a credential rotation that left a stale secret => stale credential).2204. contributing_alert_issue: search `/api/enterprise/messages` by client name and221 "alert"/"archive". If the alerting message landed in an archived alert channel222 (channel name containing "archive") so the team missed it ->223 ARCHIVED_ALERT_ROUTE. Else NONE (use UNKNOWN only if truly indeterminate).2245. sla_credit_percent = `/api/enterprise/sla/<enterprise_account_id>`225 monthly_export_credit_percent (an integer percent). Confirm the credit_trigger226 (e.g. "3 consecutive failed runs") is met.2276. severity = the incident's severity enum (Critical | High | Medium | Low).2287. engineering_owner / account_owner = incident's owner user ids. finance_owner comes229 from the enterprise account record (used for share permissions / finance review).2308. channel_name / evidence_folder / report_title: build from the response_requirements231 `naming_style` exactly (lowercase-hyphen channel; client+date investigation folder;232 "<Client> Export Failure Report" style title). Match the requested casing/format233 token-for-token; these are exact-string-scored and easy to lose points on.2349. share_permissions: include exactly the users in `permission_users_to_include`, in the235 listed order, each with a permission from {view, edit, upload_only}.23610. response_status: if a financial SLA credit must be approved before sending ->237 NEEDS_FINANCE_REVIEW; if engineering sign-off is still pending ->238 NEEDS_ENGINEERING_REVIEW; if the incident is still being investigated ->239 UNDER_INVESTIGATION; only READY_TO_SEND when nothing is outstanding.240241Reliable fields here: incident_id, enterprise_account_id, owners, the failure window242dates+count, sla_credit_percent, severity, and contributing_alert_issue. The free-text243artifact strings (channel_name, evidence_folder, report_title) and share-permission244levels are exact-match and the biggest risk — derive them strictly from the provided245naming_style and user list rather than inventing your own convention.246247---248249## Output-format discipline (all task families)250- Return ONLY the JSON object conforming to the task's `answer_template.json`. No prose,251 no markdown fences, no extra keys.252- Preserve the exact ID ordering the template demands.253- Enums must match the template spelling EXACTLY (uppercase enum tokens, exact casing of254 severity/permission). A near-synonym scores as wrong.255- Numbers: money to two decimals, data_refuel_gb to one decimal, integers for counts and256 percents. Use 0.0 / 0.00 (not null) when "not applicable"; use "" (empty string, not257 null) for not-applicable string/ID fields.258- Recompute all summary counts from your own per-item decisions so they are internally259 consistent.260- Booleans are real JSON booleans, not strings.261262## Pitfalls learned (reusable)263- bandwidth_issue depends only on measured-vs-subscribed; it stays TRUE on escalated264 physical-fault tickets too. Do not blank issue flags just because a ticket escalates.265- A PROVISIONING_STALE ticket whose auto-fix leaves latency still elevated escalates to266 TIER2_SUPPORT and KEEPS the PROVISIONING_STALE blocker — it is not PENDING_ACTION and267 not NONE. Auto-resolution requires genuine metric recovery (latency back under ~100ms).268- GENERATED_NOISE root cause + GENERATED_CHECK step = decoy; the real classification comes269 from the upstream blocker, and diagnostic_required is false for those tickets.270- For enterprise packages, the deterministic API-derived fields are dependable; sink your271 effort into matching the requested naming conventions and permission levels exactly,272 since those exact-string fields are where points silently leak.