← Tidelines/Deep dives

Your agent has never seen your app

The troubleshooting section tells the agent to walk the customer through the app. The agent has no screen, no platform and no idea which release the customer is on. It is talking someone through a room it has only had described to it, once, by a person on a different phone.

by TypeGlish team7 min read#deep-dives
Tap the gear. Which gear?

TL;DR A support agent cannot see the app it guides customers through, so let the host choose the platform's path at build time, give one step per reply and ask what the screen shows, and let a device or account tool, not the agent's guess, decide whether the fix worked.

Every contact centre has a tech who is brilliant on the phone with an app they cannot see. They ask which phone you have. They ask what is on the screen right now. They give one instruction and wait. They know the app changed in the spring, so they check the version before they describe a button. None of that habit is in your agent unless the prompt puts it there, and most troubleshooting sections are written as if the agent were looking over the customer's shoulder.

§1The reframe: guiding blind

Think of your agent as a tech on the phone with the lights off. It is missing three things a person beside the customer would have.

  • Which app. iOS and Android rarely put the same setting in the same place, and the web dashboard is a third layout. The prompt usually describes one of them, whichever phone its author owned.
  • What is on the screen now. The customer may be on the wrong tab, behind a permissions pop-up, or on a release where the menu moved. The agent learns any of that only if it asks.
  • Whether the step worked. Done, still nothing could mean the step failed, the step was skipped, or the step worked and the problem is somewhere else.

Each gap has an owner who can fill it, and in each case that owner is not the model. The host app knows the platform. The customer knows the screen. The device knows whether it is online. The prompt's job is to route each question to whoever can answer it.

The prompt is the only screen your agent has ever seen, and it was a screenshot.

§2A troubleshooting section that assumes a screen

Heron makes smart thermostats, and most of its chat volume is the thermostat dropping off Wi-Fi. Its troubleshooting section was written by a product manager with an iPhone, from memory of the app.

heron-before.tg - the section as it ships✗ C (71/100)
# Role
You are the support assistant for Heron, a smart thermostat maker.

# Wi-Fi troubleshooting
- Walk the customer through reconnecting the thermostat to Wi-Fi.
- Tell the customer to tap the gear icon in the top right, then Wi-Fi, then Reconnect.
- If the customer can't find it, explain where it is.
- Make sure the customer has finished each step before moving on.
- The settings button is in the top right corner.
tg review - heron-before.tg✗ 0 of 5 rules read
$ typeglish review heron-before.tg
heron-before.tg — C (71/100) → A (96) projected if every fix below lands  check: 0 error, 0 warning, 7 info  tiers: base+z3

  FIXES  ranked by projected gain
  +12  put a "@@ why" note above L2 L5 L6 L7 L8 L9 — annotation 0 → 100
  +10  lead with a modal (MUST / NEVER / SHOULD) on L5 L6 L7 L8 — 4 rules become visible to the checker
  +2   fix the 2 prompt/unintroduced-definite findings (L6 L9) — A definite retrieves a card the document never introduces (Open the store. - which store?) - introduce the head on an…
  +1   fix the 1 typeglish/if-then finding (L7) — A conditional (IF / WHEN / UNLESS / WHILE / …) opened without its THEN — an ADVISORY by the owner’s ruling (2026-08,…

  RULES  0 of 5 rule-shaped lines read
    L5   ✗ invisible     Walk the customer through reconnecting the thermostat to Wi-Fi.
                         no modal (MUST / NEVER / SHOULD) and "Walk" is not a verb the checker knows, so it reads as prose → lead with a modal, or start with a verb from the catalog (Escalate …, Ask …)
    L6   ✗ invisible     Tell the customer to tap the gear icon in the top right, then Wi-Fi, then Reconnect.
                         no modal (MUST / NEVER / SHOULD), and the checker knows "tell" but cannot prove this shape: it stops at "," — the tail after it is not part of the provable rule, so it reads as prose → lead with a modal (MUST …) to make it a rule, and one action, one object per line; give the tail its own line to make it provable
    L7   ✗ invisible     If the customer can't find it, explain where it is.
                         the condition has no THEN branch, so nothing is required when it holds → add THEN MUST / NEVER / SHOULD <verb> …
    L8   ✗ invisible     Make sure the customer has finished each step before moving on.
                         no modal (MUST / NEVER / SHOULD), and the checker knows "make" but cannot prove this shape: it stops reading at "before" — the clause after it is not part of the provable action, so it reads as prose → lead with a modal (MUST …) to make it a rule, and if the clause is a condition, make it one: WHEN <clause> THEN MUST <verb> …; otherwise give it its own rule to make it provable
    L9   = fact          The settings button is in the top right corner.
                         a declared fact, not a rule — the world model reads it; it has no modal to enforce

  FINDINGS
  heron-before.tg:5:1  info   prompt/unparsed-rule  Reads like a rule, but the rule reader cannot parse it as one, so it earns no Enforceability credit and no measurability check: no modal (MUST / NEVER / SHOULD) and "Walk" is not a verb the checker knows, so it reads as prose. Fix: lead with a modal, or start with a verb from the catalog (Escalate …, Ask …).
  heron-before.tg:6:1  info   prompt/unparsed-rule  Reads like a rule, but the rule reader cannot parse it as one, so it earns no Enforceability credit and no measurability check: no modal (MUST / NEVER / SHOULD), and the checker knows "tell" but cannot prove this shape: it stops at "," — the tail after it is not part of the provable rule, so it reads as prose. Fix: lead with a modal (MUST …) to make it a rule, and one action, one object per line; give the tail its own line to make it provable.
  heron-before.tg:6:1  info   prompt/unintroduced-definite  "the gear icon" retrieves something this document never introduces - a model must guess which icon is meant. Mention an icon on an earlier line, or anchor it here ("their icon", "the icon you ...").
  heron-before.tg:7:1  info   typeglish/if-then  IF needs a THEN — write IF <condition> THEN <action>.
  heron-before.tg:7:1  info   prompt/unparsed-rule  Reads like a rule, but the rule reader cannot parse it as one, so it earns no Enforceability credit and no measurability check: the condition has no THEN branch, so nothing is required when it holds. Fix: add THEN MUST / NEVER / SHOULD <verb> ….
  heron-before.tg:8:1  info   prompt/unparsed-rule  Reads like a rule, but the rule reader cannot parse it as one, so it earns no Enforceability credit and no measurability check: no modal (MUST / NEVER / SHOULD), and the checker knows "make" but cannot prove this shape: it stops reading at "before" — the clause after it is not part of the provable action, so it reads as prose. Fix: lead with a modal (MUST …) to make it a rule, and if the clause is a condition, make it one: WHEN <clause> THEN MUST <verb> …; otherwise give it its own rule to make it provable.
  heron-before.tg:9:1  info   prompt/unintroduced-definite  "the top right corner" retrieves something this document never introduces - a model must guess which corner is meant. Mention a corner on an earlier line, or anchor it here ("their corner", "the corner you ...").

  TESTS  no $TEST cases — add one beside a rule; `test --dry` validates it offline
No errors, nothing blocked, and not one of the four instructions is a rule the checker can read. The only line it does read is a fact about the top right corner, and that fact is true on one platform.

Read it as the blind tech would. Line 6 describes a screen: a gear, top right, three taps. Line 9 restates the location as a fact, so the model holds it as true for every customer. Line 7 says that when the customer cannot find the gear, the agent should explain where it is, which means repeating line 9 to someone who has just told you it is not there. Line 8 asks the agent to make sure each step is finished, and the only way it can make sure of anything is to ask.

So when an Android customer says there's no gear, the prompt has given the agent one answer: it's in the top right. The model is not malfunctioning when it says that twice. It is describing the only screen it was shown.

§3Give each gap to its owner

The rebuild follows the reframe one gap at a time.

  • Which app: the host decides. The chat widget already knows whether it is running inside the iOS or the Android app. Declare it as $REQUIRE variable platform: one of ios, android and put each path in a $SWITCH ON arm. The compiler picks the arm before the model reads anything, so the model never sees the other platform's screen, let alone has to guess which one applies.
  • What is on the screen: the customer reports. One step per reply, and a rule that sends the agent back to asking when a step fails, instead of re-explaining.
  • Whether it worked: the device answers. A thermostat can report whether it is online. A $TOOL that reads that report outranks both the customer's impression and the agent's hope.
heron.tg - the section rebuilt✓ A (96/100)
$REQUIRE variable platform: one of ios, android

$TOOL get_device_status
  - description:: Read the thermostat's own report: online or offline, and the Wi-Fi network it last joined.
  - input::
    - serial_number:: string
$TOOL transfer_to_tech_support
  - description:: Transfer the chat to the technical support team.

# Role
@@ role: a thermostat maker's chat desk; every later "you" is the agent
You are the support assistant for Heron, a smart thermostat maker.

# Wi-Fi troubleshooting
@@ one_step: the agent cannot watch the customer tap, so each reply carries one step and waits for the customer's report
- MUST give at most 1 step per reply.
@@ ask_screen: the customer's description is the only view of the screen the agent gets
- WHEN the customer says a step did not work THEN MUST ask the customer to describe their screen.
@@ device_first: the thermostat can report its own connection, which beats the customer's guess
- WHEN the customer reports a Wi-Fi problem THEN MUST call @[get_device_status].
@@ device_decides: the device report, not the customer's screen, says whether the fix worked
- IF @[get_device_status] reports the thermostat offline after the last step THEN MUST call @[transfer_to_tech_support].

@@ app_path: the host knows which app the customer is using; the compiler picks the path before the model reads anything
$SWITCH ON @{platform}
  - ios:: In the Heron app, the path is Settings, then Wi-Fi, then Reconnect.
  - android:: In the Heron app, the path is Menu, then Devices, then Wi-Fi, then Reconnect.

$TEST still_offline
  - input:: My thermostat says offline. I tapped Reconnect like you said and nothing happened.
  - expect::
    - calls get_device_status
    - asks the customer to describe their screen
    - gives at most 1 step
    - transfers to tech support if the thermostat is still offline
    - matches /^(?![\s\S]*\btop right\b)/i
tg review and test --dry - heron.tg✓ 4 of 6 rule-shaped lines read
$ typeglish review heron.tg
heron.tg — A (96/100)  check: 0 error, 0 warning, 0 info  tiers: base+z3

  FIXES  none — every facet is essentially full

  RULES  4 of 6 rule-shaped lines read · 4/4 covered by $TEST
    L26  · arm           ios:: In the Heron app, the path is Settings, then Wi-Fi, then Reconnect.
                         a $SWITCH / $IF arm body — selected at compile time; the checker does not read arm bodies as rules
    L27  · arm           android:: In the Heron app, the path is Menu, then Devices, then Wi-Fi, then Reconnect.
                         a $SWITCH / $IF arm body — selected at compile time; the checker does not read arm bodies as rules

  TESTS  1 case · 4/4 rules exercised

$ typeglish test heron.tg --dry
✓ heron.tg  coverage: 4/4 rules exercised
  · still_offline — "My thermostat says offline. I tapped Reconnect like you said" (not run)
      ✓ matches /^(?![\s\S]*\btop right\b)/i
      · rubric — 4 prose expectations, judged on a live run (needs ANTHROPIC_API_KEY)
✓ 1 prompt — 0 failed
C to A. The two arm bodies are listed but not read as rules: they are selected at compile time, so there is nothing for the model to weigh. The regex assert fails any live reply that falls back on the old top-right description.

Build it once per platform and look at what the model actually receives. Each build carries one path, and neither mentions the other:

tg build - one screen per build✓ two artifacts
$ typeglish build heron.tg --vars '{"platform":"ios"}' && tail -1 .typeglish/dist/heron.txt
✓ built .typeglish/dist/heron.txt ← heron.tg (ec3fd51c412c, full)
In the Heron app, the path is Settings, then Wi-Fi, then Reconnect.

$ typeglish build heron.tg --vars '{"platform":"android"}' && tail -1 .typeglish/dist/heron.txt
✓ built .typeglish/dist/heron.txt ← heron.tg (7a5661e1ee3b, full)
In the Heron app, the path is Menu, then Devices, then Wi-Fi, then Reconnect.
Two hashes, two prompts. The Android customer's agent has never heard of a gear in the top right, so it cannot repeat one.

This is the compile-time plane doing the job it exists for, the same split described in Your system prompt has a compile time: a fact the host already knows should never be a question the model answers.

§4The screen you have not built yet

The reframe pays off the day the product changes. Heron ships a web dashboard, and somebody adds web to the platform input so the chat widget on the website can say where it is running. They do not touch the troubleshooting section, because nobody remembers it describes screens.

tg check - a third screen nobody described✗ build refused
$ diff heron.tg heron-web.tg
1c1
< $REQUIRE variable platform: one of ios, android
---
> $REQUIRE variable platform: one of ios, android, web
$ typeglish check heron-web.tg
heron-web.tg:25:1  error  structure/non-exhaustive-switch  @{platform} can be web, but no arm handles it — add a "- <member>::" row for each (or a deliberate "- otherwise::" fallback).

✗ 1 file — 1 error, 0 warning, 0 info
One word added to the input, and the switch that describes screens refuses to build until somebody writes the web path.

In the old prompt, the same change would have shipped silently. The web customer would have been told to tap a gear in the top right of a page that has a sidebar. With the paths in a switch, the new screen is a compile error that names the line where its description belongs. The same mechanism makes a missing tool outcome a build error in How to write what your agent says when the lookup fails.

The check covers the platforms, not the releases. If the iOS app moves Wi-Fi under a new Devices tab next spring, the ios arm is simply wrong, and no checker can know that. What the reframe buys you there is a single place to look: the path lives in one arm, owned by whoever ships the app, rather than in a sentence and a fact four lines apart. The ask_screen rule is the runtime safety net for the release you have not caught up with yet.

§5Common questions

Why does my support agent give the wrong steps for our app?
Because it has never seen the app. It knows only the screen your prompt describes, which is one platform at one release. When the customer is on the other platform, or the app has been redesigned since the prompt was written, the agent describes a screen that is not there.
How do I write app troubleshooting steps in a system prompt?
Let the host pass the platform as a typed input and keep one path per platform in a $SWITCH ON block, so the build ships only the path for this customer. Then cap the agent at one step per reply, ask the customer to describe their screen when a step fails, and check the result with a device or account tool.
Can TypeGlish tell me when a new platform has no troubleshooting steps?
Yes, when the platform is a typed input and the steps live in a $SWITCH ON block. Add a member such as web to the domain without adding its arm, and the check fails with structure/non-exhaustive-switch. Steps written as prose for one platform are never compared to anything.
Should my agent trust the customer when they say a step worked?
Treat it as a report, not a result. Where a tool can read the device or the account directly, call it after the last step and let its answer decide whether the issue is fixed or needs a transfer. The customer's description is still the only view the agent has of the screen.
Field note

Search your prompt for screen words: tap, click, button, icon, menu, top right, page, tab. Every hit is a description of one screen, and you should be able to say which platform and which release it was copied from. Move each into a $SWITCH ON arm keyed to a platform input the host fills, then run typeglish check once with a member added that you have no arm for, to watch the build refuse. For the one-step rule, How to make your agent ask one question at a time shows the same bound written as a counted test.

∿ washed up Oct 31, 2026 ∿