Chapter 8: Confidence and Confidence-Gated Routing

Learning Objectives

Pre-Quiz: What Confidence Measures

Two incidents both return choice = "dc_fabric", but Incident A reports confidence ~0.93 and Incident B reports confidence ~0.38. What does this difference indicate?

Incident A's probability distribution is peaked on dc_fabric while Incident B's is flat across several options, even though both picked the same top option
Incident B used a different, less capable model than Incident A
Incident A's probabilities don't sum to 1.0 while Incident B's do
The choice field is unreliable and should be ignored whenever confidence is reported

Why does the chapter recommend inspecting the full probabilities field rather than confidence alone when confidence is low?

Because probabilities reveal how the model is uncertain — e.g., a near two-way tie can be routed to a combined queue, while a genuine four-way scatter usually needs a human
Because the confidence field is only available on Score answers, not Choice answers
Because probabilities are always more accurate than the confidence statistic
Because TypeSafe recomputes confidence incorrectly and probabilities are the corrected version

A ticket owner question consistently splits confidence roughly 50/50 between network_core and dc_fabric across many different incidents. What does the chapter say this pattern most likely indicates?

A criteria defect — the two options' descriptions probably overlap and need clearer what/not_for/examples boundaries
The incidents are genuinely ambiguous and no fix is possible
The model needs to be replaced with a higher-parameter version
The severity Score question is miscalibrated and should be removed

What is the key difference between this chapter's confidence ladder and Chapter 7's Noul-probability ladder?

Confidence measures how concentrated a Choice/Score answer's distribution is, while Noul probability measures the likelihood that a statement is true — different quantities on the same 0-to-1 scale
They are the same measurement, just renamed in different chapters for clarity
Noul probability only applies to destructive actions, while confidence only applies to read-only actions
Confidence is always higher than Noul probability for the same action

In the SEC filings example, uncertain predictions forced into specific categories were 40% accurate, while the same predictions reported one level up the hierarchy (as divisions) were 70% accurate. What lesson does the chapter draw from this?

Hierarchical fallback — degrading a low-confidence answer to a coarser but more reliable label nearly doubles accuracy on exactly the cases the system would otherwise get wrong
Confidence scores should never be trusted below 0.9
The model should always be asked to reconsider with a second validation call when confidence is low
SEC filings are a poor use case for TypeSafe and should be routed entirely to humans

What Confidence Measures

Key Points

TypeSafe's confidence works like a five-minute utilization average: it collapses many possible values into one summary figure, and is therefore useful but lossy. Every Choice and Score answer already carries a full probability distribution, and confidence is a statistic computed from it — a property of numbers the model already produced, not a heuristic bolted on afterward.

Peaked Versus Flat Distributions

The shape that matters is concentration. A flat shape, with probability spread across several options, means low confidence. A single peak on one option means high confidence. Two answers to the same NOC triage question illustrate the point:

Team optionIncident A: peakedIncident B: flat
dc_fabric0.910.34
network_core0.050.29
wan_transport0.030.22
wireless0.010.15
Selected choicedc_fabricdc_fabric
Reported confidence~0.93 (high)~0.38 (low)

Both answers return the same choice, because choice is simply the highest-probability option. If your code reads only choice, the two incidents are indistinguishable — but Incident A is a fabric event that could hardly be anything else, while Incident B is a 34% guess in a four-way race where random guessing scores 25%.

Figure 8.1: The same choice, two distributions

Incident A — peaked                    Incident B — flat
confidence ~0.93                       confidence ~0.38

dc_fabric     ####################     dc_fabric     #######
              0.91                                   0.34
network_core  #                        network_core  ######
              0.05                                   0.29
wan_transport |                        wan_transport ####
              0.03                                   0.22
wireless      |                        wireless      ###
              0.01                                   0.15

choice = dc_fabric                     choice = dc_fabric

Same selected label, same field value, entirely different events. The left one is a fabric alert that could hardly be anything else. The right one is a 34% guess in a four-way race where blind guessing scores 25%.

Visual animation — coming soon

Confidence as a Summary; Probabilities as the Full Picture

Confidence is the five-minute average; probabilities is the per-sample detail. Consider two low-confidence answers that both report roughly 0.4:

{
  "owner": {
    "choice": "dc_fabric",
    "probabilities": {
      "dc_fabric": 0.46,
      "network_core": 0.44,
      "wan_transport": 0.06,
      "wireless": 0.04
    },
    "confidence": 0.41
  }
}

...versus a genuinely four-way split at 0.30 / 0.26 / 0.23 / 0.21. The first is a two-horse race: the model has firmly ruled out wireless and transport and cannot separate fabric from core — actionable, route to a combined queue. The second is noise, and the right move is a human.

This is the hierarchical fallback idea: across 60 SEC filings with a threshold at 0.9, confident predictions were 90% accurate, uncertain predictions forced to be specific were 40% accurate, and those same uncertain predictions reported one level up as divisions were 70% accurate. Degrading gracefully nearly doubled the accuracy of exactly the cases the system would otherwise get wrong.

Low Confidence as a Signal the Question or State May Be Wrong

Persistent low confidence deserves the same suspicion network engineers apply to an impossible interface counter — check whether you polled the right OID before blaming the device. Three causes account for most of it:

One clarification worth holding onto for the rest of the chapter and the next: this chapter's confidence ladder is not the same thing as Chapter 7's Noul-probability ladder. Noul probability scores the likelihood that a statement is true; confidence scores how concentrated a Choice or Score answer's distribution is. They are different quantities that happen to share the same 0-to-1 range, so a given Noul probability value and a confidence value of the same magnitude are not interchangeable — the two ladders are tuned independently. Where the action class is the same, the two tables agree by design: read-only enrichment clears at 0.50 on both, routing and assignment at 0.60, and anything destructive at 0.95 plus corroboration.

Key Takeaway: Confidence is a statistic that collapses the shape of an answer's probability distribution into a single number from 0 to 1 — peaked means high, flat means low — computed from probabilities the model already produced, not from self-assessment. Route on confidence, but inspect probabilities when you need to know how the model is uncertain, because a two-way split and a four-way split call for different fallbacks. Persistently low confidence on a question is a defect report about your criteria or your state, not about the model.
Post-Quiz: What Confidence Measures

Two incidents both return choice = "dc_fabric", but Incident A reports confidence ~0.93 and Incident B reports confidence ~0.38. What does this difference indicate?

Incident A's probability distribution is peaked on dc_fabric while Incident B's is flat across several options, even though both picked the same top option
Incident B used a different, less capable model than Incident A
Incident A's probabilities don't sum to 1.0 while Incident B's do
The choice field is unreliable and should be ignored whenever confidence is reported

Why does the chapter recommend inspecting the full probabilities field rather than confidence alone when confidence is low?

Because probabilities reveal how the model is uncertain — e.g., a near two-way tie can be routed to a combined queue, while a genuine four-way scatter usually needs a human
Because the confidence field is only available on Score answers, not Choice answers
Because probabilities are always more accurate than the confidence statistic
Because TypeSafe recomputes confidence incorrectly and probabilities are the corrected version

A ticket owner question consistently splits confidence roughly 50/50 between network_core and dc_fabric across many different incidents. What does the chapter say this pattern most likely indicates?

A criteria defect — the two options' descriptions probably overlap and need clearer what/not_for/examples boundaries
The incidents are genuinely ambiguous and no fix is possible
The model needs to be replaced with a higher-parameter version
The severity Score question is miscalibrated and should be removed

What is the key difference between this chapter's confidence ladder and Chapter 7's Noul-probability ladder?

Confidence measures how concentrated a Choice/Score answer's distribution is, while Noul probability measures the likelihood that a statement is true — different quantities on the same 0-to-1 scale
They are the same measurement, just renamed in different chapters for clarity
Noul probability only applies to destructive actions, while confidence only applies to read-only actions
Confidence is always higher than Noul probability for the same action

In the SEC filings example, uncertain predictions forced into specific categories were 40% accurate, while the same predictions reported one level up the hierarchy (as divisions) were 70% accurate. What lesson does the chapter draw from this?

Hierarchical fallback — degrading a low-confidence answer to a coarser but more reliable label nearly doubles accuracy on exactly the cases the system would otherwise get wrong
Confidence scores should never be trusted below 0.9
The model should always be asked to reconsider with a second validation call when confidence is low
SEC filings are a poor use case for TypeSafe and should be routed entirely to humans
Pre-Quiz: Thresholds, Tiers, and Risk

The confidence.md and confidence-routing.md pages give slightly different numbers for the three-tier bands (e.g., high above 0.9 vs above 0.85). What point does the chapter make about this discrepancy?

The bands are illustrative starting points, not fixed settings — organizations should start conservative and adjust thresholds based on their own measured performance
One of the two documentation pages is outdated and should be ignored
The discrepancy proves TypeSafe's confidence scores are not reproducible
0.85 applies to Choice answers and 0.9 applies to Score answers

In the medium confidence tier, why does the chapter say auto-assigning an incident to a team can proceed automatically across the whole 0.60–0.85 band, while auto-resolving (closing) an incident should not?

Assignment is cheaply and quickly reversible (a human just reassigns it), while auto-resolution is effectively irreversible because a wrongly closed incident has no natural detection path
Assignment never affects customer-visible fields, while resolution always does
ServiceNow's API technically prevents PATCHing the state field below 0.85 confidence
Auto-resolution is cheaper to compute than assignment, so it warrants more caution

According to the risk-scaled threshold ladder, which starting threshold is correctly matched to its action class?

Assign/route an incident to a team: 0.60
State change/notify (e.g., paging on-call): 0.60
Read-only enrichment such as tagging: 0.85
Auto-resolve (close) an incident: 0.60

Why does the chapter argue that auto-resolving an incident deserves a stricter confidence gate (0.95+) than pushing a config change (0.90+), even though a config change can affect live production traffic?

A bad config change tends to announce itself through alarms, but a wrongly closed incident looks handled and may go unnoticed for hours — it has no natural detection path
Config changes are always reviewed by a human before being pushed, so they need a lower bar
Auto-resolution requires more API calls than a config change, increasing the chance of error
ServiceNow does not log auto-resolution actions, so the extra confidence compensates for the missing audit trail

A team builds a confidence-vs-accuracy bucket table from historical tickets and finds a 13-point accuracy drop between the 0.75–0.84 bucket (84% accurate) and the 0.60–0.74 bucket (71% accurate) — the steepest drop in the table. What does the chapter say to do with this finding?

Treat the cliff as a natural threshold location — a floor placed just above it captures most available coverage while avoiding the bucket where accuracy degrades fast
Ignore it, because cliffs in reliability diagrams are statistical noise and not actionable
Immediately lower all thresholds to 0.60 to capture more automation coverage
Conclude the model is fundamentally uncalibrated and abandon confidence-based routing

The Three-Tier Threshold Pattern

Key Points

The pattern fits in one sentence: "The answer tells you what; confidence tells you whether to act." Confidence-gated routing uses the confidence score as a decision filter alongside the answer, so the same choice value leads to different behavior depending on how strongly the model held it — the QoS-classification analogy: a marking doesn't change what a packet contains, only what the network is willing to do with it.

Tierconfidence.md guidanceconfidence-routing.md guidanceWhat your automation does
HighAbove 0.9: act automatically for high-stakes decisionsAbove 0.85: high-risk operations execute automaticallyWrite the change, close the ticket, no human touches it
Medium0.5 to 0.9: proceed cautiously; consider confirmation0.6 to 0.85: lower-risk actions proceed automatically; higher-stakes actions request confirmationAct, but leave an audit trail; or act only on the low-risk subset
LowBelow 0.5: route to humans; genuine uncertaintyBelow 0.6 (the floor): route to human support agentsHand off to a person or a deterministic fallback

These are starting points, not settings — "the correct threshold values depend on your domain and the performance of the model for your use case." The two pages disagreeing slightly is itself the lesson: illustrative bands you replace with measured ones.

Figure 8.2: Three-tier confidence-gated routing

flowchart TD A["Model produces confidence score"] --> B{"Confidence tier"} B -->|"High: above 0.85 to 0.9"| C["Act automatically"] B -->|"Medium: 0.5 to 0.9 or 0.6 to 0.85"| D["Act cautiously or request confirmation"] B -->|"Low: below 0.5 or 0.6"| E["Route to a human"] C --> F["No human involvement"] D --> G["Audit trail or confirmation step"] E --> H["Human review or coarser fallback"]

High Confidence: Act Without Human Involvement

The high tier is for answers where the probability mass sits on one option and the action is worth automating. In the NOC service, an access point that deauthenticates and re-associates inside a known maintenance window — a pattern closed as noise hundreds of times — should not consume a human minute at 0.97 confidence. Two rules keep this tier safe: write the confidence value and question name onto every automated action for auditability, and sample it — a random 2% of auto-actioned tickets into a weekly human review queue is how you learn a 0.97 answer stopped being 97% accurate.

Medium Confidence: Proceed with Caution or Flag

The middle tier is where most real traffic lands. Between 0.6 and 0.85, lower-risk actions such as checking an account balance proceed automatically, while higher-stakes actions request confirmation rather than acting independently. In incident terms, assigning to a team is the balance check — cheap to get wrong, trivially reversible — while auto-resolving is the transfer approval, where a wrong call closes a real outage silently. So the medium tier performs the reversible action and attaches a review flag: a work note carrying the confidence value, the runner-up option, and a marker asking a human to confirm.

Low Confidence: Route to a Human or a Fallback System

Below the floor, the model is telling you it does not know. "Route to a human" has a formal name — selective prediction, where a system may abstain rather than predict, with explicit risk control so the review queue is not overwhelmed. The binding constraint is capacity: if 30% of alerts fall below the floor and the night shift is two people, you have built a queue nobody drains, not a safety net. As the human queue lengthens, the confidence threshold for automation must decrease, meaning the bar for escalation must rise — a counter-cyclical rule, best expressed as a runtime value rather than a constant compiled into the triage service.

Key Takeaway: The three-tier pattern separates what the model answered from whether you should act on it: act automatically at high confidence, act reversibly with an audit flag in the middle, and hand off to a human or a coarser fallback below the floor. The documented bands — roughly above 0.85 or 0.9, a 0.5-to-0.9 or 0.6-to-0.85 middle, and a 0.5 or 0.6 floor — are explicitly starting points that depend on your domain and your model's performance. Size the low tier to the humans who must actually drain it, and make the thresholds runtime-adjustable so you can raise the escalation bar when the queue is drowning.

Scaling Thresholds to Risk

One threshold for a whole service is the most common mistake in this pattern. Different actions within the same system should be gated at different levels depending on the consequences of getting it wrong — matching thresholds to consequences prevents both over-caution (rejecting valid high-confidence requests) and dangerous under-caution (acting on uncertain high-risk decisions). The mental model is a change management process: a show command needs no approval, a VLAN description edit needs a peer, and a BGP policy change needs a CAB review.

Action classExampleBlast radius if wrongReversibilitySuggested starting threshold
Read-only / enrichTag an incident, add a work note, populate a dashboardNone — a wrong label a human overridesInstant, free0.50
Assign / routePATCH assignment_group on a ServiceNow incidentMinutes of the wrong team's attentionReassign, seconds0.60
State change / notifyMove to In Progress; page the on-call engineerWakes a person; consumes an escalationCannot un-page; apologize0.85
Config changePush a QoS or ACL change to an IOS-XE or EOS deviceCan affect live trafficRollback window, minutes to hours0.90+
Destructive / terminalAuto-resolve an incident; suppress an alert classA real outage closed silently, unnoticed for hoursEffectively none0.95+

The last row surprises people: auto-resolving a ticket feels gentler than pushing a config change, so teams gate it loosely — but it deserves the strictest gate because it is the only action with no natural detection path. A bad config change announces itself through alarms; a wrongly closed incident announces itself when a customer calls three hours later.

Figure 8.3: Risk-scaled threshold ladder

flowchart LR A["Read-only or enrich: 0.50"] --> B["Assign or route: 0.60"] B --> C["State change or notify: 0.85"] C --> D["Config change: 0.90 or higher"] D --> E["Destructive or terminal: 0.95 or higher"]

Start Conservative, Measure, Then Loosen

  1. Shadow mode, two weeks. Ask the questions on every incident and log answer and confidence, but take no action.
  2. Read-only tier only. Turn on tagging and enrichment at 0.50. Nothing can break.
  3. Assignment at a deliberately high bar. Enable auto-assignment at 0.90, and measure the reassignment rate.
  4. Loosen in steps, measuring each. Drop to 0.85, then 0.80, then 0.75, and stop when the reassignment rate crosses what your team tolerates.
  5. Auto-resolution last, on one narrow class. Enable it for a single well-understood noise pattern at 0.95, with 100% audit sampling for the first month.

Plotting Confidence Against Accuracy on Historical Tickets

Everything above depends on one table you can build from tickets you already closed — a reliability diagram: partition predictions into roughly ten equal-frequency bins, plot each bin's average predicted probability against the accuracy actually observed, and compare against the ideal diagonal where confidence equals accuracy.

Confidence bucketIncidentsModel agreed with final ownerCumulative coverage above bucket floor
0.95 – 1.0081298%30%
0.90 – 0.9443095%46%
0.85 – 0.8936191%60%
0.75 – 0.8440284%74%
0.60 – 0.7435571%87%
0.50 – 0.5918858%94%
Below 0.5015241%100%

Read three things off it. The model is roughly calibrated — accuracy tracks confidence bucket by bucket. Pick each threshold by naming the accuracy you require: if auto-resolution must be 98% correct, the table says 0.95 and you automate 30% of volume. And find the cliff — the steepest drop in the table (13 points, between the 0.75–0.84 and 0.60–0.74 buckets) is a natural threshold location, since just above it you capture most available coverage and just below it accuracy degrades fast. Rebuild the table monthly — it is your drift detector.

Figure 8.4: The threshold-tuning loop

flowchart TD A["Label historical tickets"] --> B["Run questions against ticket state"] B --> C["Bucket answers by confidence"] C --> D["Measure accuracy per bucket"] D --> E["Set thresholds from required accuracy"] E --> F["Deploy thresholds"] F --> G["Monitor for drift"] G --> A

Visual animation — coming soon

Key Takeaway: Different actions in the same service need different thresholds, scaled to blast radius and reversibility — roughly 0.50 for read-only enrichment, 0.60 for assignment, 0.85 for state changes and paging, and 0.95 or higher for auto-resolution, which is terminal and therefore the strictest gate despite feeling gentle. Start above those numbers, run in shadow mode to collect labels, and loosen one step at a time while watching the reassignment rate. Pick the final values from a confidence-versus-accuracy bucket table built on your own closed tickets, and rebuild it monthly to catch drift.
Post-Quiz: Thresholds, Tiers, and Risk

The confidence.md and confidence-routing.md pages give slightly different numbers for the three-tier bands (e.g., high above 0.9 vs above 0.85). What point does the chapter make about this discrepancy?

The bands are illustrative starting points, not fixed settings — organizations should start conservative and adjust thresholds based on their own measured performance
One of the two documentation pages is outdated and should be ignored
The discrepancy proves TypeSafe's confidence scores are not reproducible
0.85 applies to Choice answers and 0.9 applies to Score answers

In the medium confidence tier, why does the chapter say auto-assigning an incident to a team can proceed automatically across the whole 0.60–0.85 band, while auto-resolving (closing) an incident should not?

Assignment is cheaply and quickly reversible (a human just reassigns it), while auto-resolution is effectively irreversible because a wrongly closed incident has no natural detection path
Assignment never affects customer-visible fields, while resolution always does
ServiceNow's API technically prevents PATCHing the state field below 0.85 confidence
Auto-resolution is cheaper to compute than assignment, so it warrants more caution

According to the risk-scaled threshold ladder, which starting threshold is correctly matched to its action class?

Assign/route an incident to a team: 0.60
State change/notify (e.g., paging on-call): 0.60
Read-only enrichment such as tagging: 0.85
Auto-resolve (close) an incident: 0.60

Why does the chapter argue that auto-resolving an incident deserves a stricter confidence gate (0.95+) than pushing a config change (0.90+), even though a config change can affect live production traffic?

A bad config change tends to announce itself through alarms, but a wrongly closed incident looks handled and may go unnoticed for hours — it has no natural detection path
Config changes are always reviewed by a human before being pushed, so they need a lower bar
Auto-resolution requires more API calls than a config change, increasing the chance of error
ServiceNow does not log auto-resolution actions, so the extra confidence compensates for the missing audit trail

A team builds a confidence-vs-accuracy bucket table from historical tickets and finds a 13-point accuracy drop between the 0.75–0.84 bucket (84% accurate) and the 0.60–0.74 bucket (71% accurate) — the steepest drop in the table. What does the chapter say to do with this finding?

Treat the cliff as a natural threshold location — a floor placed just above it captures most available coverage while avoiding the bucket where accuracy degrades fast
Ignore it, because cliffs in reliability diagrams are statistical noise and not actionable
Immediately lower all thresholds to 0.60 to capture more automation coverage
Conclude the model is fundamentally uncalibrated and abandon confidence-based routing
Pre-Quiz: Worked Example — Auto-Close, Assign, or Page

In the triage() function, why does one client.system_one(...) call ask both a Choice (owner) and a Score (severity) question together rather than making two separate calls?

Asking multiple questions in a single request costs minimal additional tokens, and the two answers together decide both "where" to route and "how loudly" to escalate
The TypeSafe SDK does not support making more than one API call per incident
ServiceNow's Table API requires exactly one PATCH per TypeSafe request
Combining questions increases the confidence score reported for each answer

Why does the chapter recommend storing routing targets like fallback_group and each entry in team_groups as ServiceNow sys_ids rather than display names?

A sys_id is the stable reference that keeps working even if someone renames the group later, whereas a display name would break
ServiceNow's PATCH endpoint only accepts sys_ids, never display names, for any field
Sys_ids are required to compute the TypeSafe confidence score correctly
Display names cannot be stored in a Python dictionary

In assign(), under what condition does the function add a "[REVIEW]" flag to the work note asking a human to confirm the assignment?

When confidence is at or above the 0.60 assignment floor but still below 0.85
Whenever the incident is routed to the known_noise option
Whenever confidence is at or above 0.95
Only when the model's top two probabilities are exactly tied

An incident's owner confidence comes back at 0.45 (below the assignment floor), and its severity Score is 4.2 (above page_min_severity). According to escalate(), what happens to the incident?

It is PATCHed to the fallback group with a work note carrying the model's best guess and distribution, AND an on-call page is fired because severity is high
It is discarded with no action, since confidence is too low to act on at all
It is auto-resolved as known noise regardless of the owner choice, since severity is high
It is assigned directly to the model's best-guess team without any review flag

Why does the chapter describe assigning an unclear incident to "Network Operations" (the parent group) in escalate() as a hierarchical fallback rather than a failure to act?

It mirrors the technique of reporting a coarser, more reliable label instead of forcing a narrow guess or rejecting the answer outright, and it keeps the model's shortlist attached to the ticket
Network Operations is the team with the highest confidence score among all four options
It is a temporary bug that the chapter recommends fixing in a future version
It only happens when the TypeSafe API call fails entirely

Worked Example: Auto-Close, Assign, or Page

Key Points

One client.system_one(...) call asks two questions about an incoming ServiceNow incident: a Choice for the owning team — with a fifth option for known transient noise — and a Score for severity. The confidence values then select one of three actions against the ServiceNow Table API. Incidents are updated with PATCH /api/now/table/incident/[sys_id]; PATCH updates only the fields you send, unlike PUT which replaces the whole record. The state field is numeric — 1 = New, 2 = In Progress, 3 = On Hold, 6 = Resolved, 7 = Closed.

Every tunable value lives in one dictionary at the top, so raising the escalation bar during a major incident is a config edit rather than a code change:

CONFIG = {
    # TypeSafe
    "model": "jev-latest",

    # ServiceNow
    "sn_instance": "https://acme.service-now.com",
    "sn_incident_table": "/api/now/table/incident",
    "sn_state_in_progress": "2",
    "sn_state_resolved": "6",
    "sn_close_code": "Solved (Permanently)",

    # Confidence thresholds -- STARTING POINTS from the TypeSafe docs,
    # replaced by values read off our own confidence-vs-accuracy table.
    "auto_resolve_min_confidence": 0.95,   # terminal action: strictest gate
    "assign_min_confidence": 0.60,         # reversible action: middle tier
    "review_flag_max_confidence": 0.85,    # below this, flag for human review

    # Severity gate for paging on low-confidence answers
    "page_min_severity": 3.0,              # index into the severity legend

    # Routing targets. Values are sys_ids from sys_user_group in YOUR
    # instance -- a group's display name also resolves, but sys_id is
    # the reference that survives someone renaming the group.
    "noise_option": "known_noise",
    "fallback_group": "<sys_id of Network Operations>",
    "team_groups": {
        "dc_fabric":     "<sys_id of DC-Fabric>",
        "network_core":  "<sys_id of Network-Core>",
        "wan_transport": "<sys_id of WAN-Transport>",
        "wireless":      "<sys_id of Wireless-NOC>",
    },
}
def triage(incident: dict) -> dict:
    response = client.system_one(state=state, questions=QUESTIONS, model=CONFIG["model"])
    owner = response.answers["owner"]
    severity = response.answers["severity"]
    sys_id = incident["sys_id"]

    # Tier 1: high confidence on known noise -> auto-resolve
    if owner.choice == CONFIG["noise_option"] and owner.confidence >= CONFIG["auto_resolve_min_confidence"]:
        return auto_resolve(sys_id, owner)

    # Tier 2: medium confidence on a real team -> assign, flag if borderline
    if owner.choice != CONFIG["noise_option"] and owner.confidence >= CONFIG["assign_min_confidence"]:
        return assign(sys_id, owner)

    # Tier 3: below the floor -> page or park for a human
    return escalate(sys_id, owner, severity)

ServiceNow Incidents: Auto-Resolve Known Noise at High Confidence

This is the strictest gate in the service, and it fires only when the model both picks known_noise and is peaked on it. The close_notes record the confidence and the runner-up, so a reviewer auditing a wrongly closed ticket sees exactly how close the call was. A 403 on the PATCH is not a transient failure to retry — it will fail forever until someone grants the integration user the role.

Assign to a Team at Medium Confidence with a Review Flag

The assignment is the reversible action, so it proceeds automatically across the whole medium band. What changes inside the band is the review flag: between 0.60 and 0.85 the work note asks a human to confirm; above 0.85 it does not. Writing the top three probabilities into the note makes graceful degradation visible — on a near-tie between two teams, the receiving engineer sees it immediately and knows where to bounce it. work_notes is the internal activity log, and u_ai_confidence is a custom field so the monthly bucket table is a database query rather than a log-parsing exercise.

Page On-Call at Low Confidence for High-Severity Guesses

Notice what the low tier does not do: throw the answer away. It assigns to Network Operations, the parent group above the four specific teams — a hierarchical fallback, reporting a coarser level rather than forcing a narrow category — and the best guess plus the full distribution travel with the ticket. The severity gate keeps paging humane: low confidence on a low-severity event is a queue item, while low confidence on something scored major or critical is a page, because an unclassified potential outage is exactly when a human must look now.

Figure 8.5: Auto-close, assign, or page worked example

sequenceDiagram participant Incident as ServiceNow Incident participant Triage as Triage Service participant TypeSafe as TypeSafe Model participant Table as ServiceNow Table API participant OnCall as On-Call Engineer Incident->>Triage: New incident created Triage->>TypeSafe: Ask owner and severity questions TypeSafe-->>Triage: Choice, Score, confidence alt High confidence known noise Triage->>Table: PATCH state to Resolved with close_code else Medium confidence real team Triage->>Table: PATCH assignment_group with work_notes else Low confidence high severity Triage->>Table: PATCH assignment_group to fallback group Triage->>OnCall: Page with confidence and distribution end

Visual animation — coming soon

Key Takeaway: A single client.system_one call carrying a Choice for ownership and a Score for severity drives three different ServiceNow actions through one confidence ladder: PATCH state to 6 with close_code and close_notes for known noise above 0.95, PATCH assignment_group with a confidence-bearing work_notes entry above 0.60, and a coarse fallback assignment plus an on-call page below the floor when severity is high. Keeping every threshold, state value, and group name in one configuration dictionary makes the escalation bar adjustable at runtime. Below the floor the answer is still written to the ticket as a shortlist — low confidence degrades the action, not the information.
Post-Quiz: Worked Example — Auto-Close, Assign, or Page

In the triage() function, why does one client.system_one(...) call ask both a Choice (owner) and a Score (severity) question together rather than making two separate calls?

Asking multiple questions in a single request costs minimal additional tokens, and the two answers together decide both "where" to route and "how loudly" to escalate
The TypeSafe SDK does not support making more than one API call per incident
ServiceNow's Table API requires exactly one PATCH per TypeSafe request
Combining questions increases the confidence score reported for each answer

Why does the chapter recommend storing routing targets like fallback_group and each entry in team_groups as ServiceNow sys_ids rather than display names?

A sys_id is the stable reference that keeps working even if someone renames the group later, whereas a display name would break
ServiceNow's PATCH endpoint only accepts sys_ids, never display names, for any field
Sys_ids are required to compute the TypeSafe confidence score correctly
Display names cannot be stored in a Python dictionary

In assign(), under what condition does the function add a "[REVIEW]" flag to the work note asking a human to confirm the assignment?

When confidence is at or above the 0.60 assignment floor but still below 0.85
Whenever the incident is routed to the known_noise option
Whenever confidence is at or above 0.95
Only when the model's top two probabilities are exactly tied

An incident's owner confidence comes back at 0.45 (below the assignment floor), and its severity Score is 4.2 (above page_min_severity). According to escalate(), what happens to the incident?

It is PATCHed to the fallback group with a work note carrying the model's best guess and distribution, AND an on-call page is fired because severity is high
It is discarded with no action, since confidence is too low to act on at all
It is auto-resolved as known noise regardless of the owner choice, since severity is high
It is assigned directly to the model's best-guess team without any review flag

Why does the chapter describe assigning an unclear incident to "Network Operations" (the parent group) in escalate() as a hierarchical fallback rather than a failure to act?

It mirrors the technique of reporting a coarser, more reliable label instead of forcing a narrow guess or rejecting the answer outright, and it keeps the model's shortlist attached to the ticket
Network Operations is the team with the highest confidence score among all four options
It is a temporary bug that the chapter recommends fixing in a future version
It only happens when the TypeSafe API call fails entirely

Your Progress

Answer Explanations