# Check the commands sent by your router-support agent

Source: https://rasa.community/library/casebook/telco-diagnostics/
Author: Rod Rivera
Published: 2026-10-22T09:00:00Z

A customer asks your support assistant to restart their shop router. They
expect a short interruption, with the Wi-Fi name and password unchanged. Your
test needs to inspect what reaches the device: the adapter could send a factory
reset even while the assistant describes a restart.

You will run Juniper Mobile’s fictional router example, trace a chosen restart
to its command log, and deliberately break one command mapping. The deliberately broken version still reports that
settings were kept. You will see why that reassuring check can pass and which
command assertion catches the fault.

You need Git and Python 3.11 or later. The command tests use Python’s standard
library. They need no model key, Rasa licence or router. The service, customer
and devices are synthetic; the results below concern local Python behaviour.

## Run the restart checks

Use a fresh checkout so you can undo the deliberate change later:

```bash
git clone https://github.com/RasaHQ/rasa-community-resources.git
cd rasa-community-resources
git checkout 4aa0c4419dc193fef7a969c12d59edcf720f2606
cd examples/mantle-text-telco-diagnostics-gpt
python3 -B -m unittest discover -s tests -p test_guard.py -k RunTests -v
```

Expect nine tests and a final `OK`. This is the completion criterion for the
command, not a claim that a router or model was tested.

The [restart tests](https://github.com/RasaHQ/rasa-community-resources/blob/4aa0c4419dc193fef7a969c12d59edcf720f2606/examples/mantle-text-telco-diagnostics-gpt/tests/test_guard.py)
check the stored selection, the displayed interruption notice and the eventual
command. A successful restart produces one `REBOOT` entry and keeps the
simulated settings. Calling the same selection again returns its receipt
without adding another command. The successful-restart test contains these
assertions against the returned command receipt:

```python
self.assertEqual(result["confirmed_operation"], "reboot")
self.assertEqual(result["command_sent"], "REBOOT")
self.assertEqual(result["commands_sent_this_conversation"], ["REBOOT"])
```

The write function builds that receipt from the same command it appends to
`net.command_log`. This inspects a local recorder, not an independent device
acknowledgement. The middle assertion is the one the deliberate mapping edit
will break.

If discovery reports zero tests, check that your working directory is the
example folder and that the checkout matches the revision above. These tests
do not install Rasa. The full suite has additional SDK checks which may skip
when its optional dependencies are absent.

## Follow the operation to the device

The example separates read-only support from changes to a router:

| Customer’s task                  | What the example permits                                                      |
| -------------------------------- | ----------------------------------------------------------------------------- |
| Check an outage or line readings | Read-only tools; no device command                                            |
| Restart the router               | Select a reboot, show its interruption notice, then obtain confirmation       |
| Erase the router’s settings      | Require an explicit request for a factory reset, then a separate confirmation |
| Ask to “reset it”                | Ask which operation the customer means                                        |

The [outage skill](https://github.com/RasaHQ/rasa-community-resources/blob/4aa0c4419dc193fef7a969c12d59edcf720f2606/examples/mantle-text-telco-diagnostics-gpt/skills/outage_status/skill.md)
has no device-changing tools. The recovery skill first checks the outage feed.
A known outage stops local recovery steps. An unavailable feed also stops them
under this example’s policy and offers another check or a technician route.
Your network-service owner must decide the appropriate policy for a real system.

When the customer chooses a restart, the
[selection function](https://github.com/RasaHQ/rasa-community-resources/blob/4aa0c4419dc193fef7a969c12d59edcf720f2606/examples/mantle-text-telco-diagnostics-gpt/lib/juniper.py)
saves the service, operation and interruption notice under a selection reference.
The Rasa wrapper stores that reference in skill memory. Its confirmation gate
holds the tool call while the customer is asked whether to proceed.

:::diagram{title="The command and settings flag come from different fields" layout="stacked"}

```dot
  rankdir=TB;
  selected [label="Selected:\nreboot"];
  catalog [label="Fixture\noperation"];
  wire [label="command"];
  destructive [label="destructive"];
  command [label="Command\nsent"];
  settings [label="Settings\nflag"];
  selected -> catalog;
  catalog -> wire -> command;
  catalog -> destructive -> settings;
```

:::

The final mapping is still part of your application. A valid selected operation
does not establish that the mapping sends the intended device command. That is
what the next test checks.

## Break the mapping deliberately

Open `lib/fixtures/juniper.json`. In the `operations` object, find `reboot` and
change only its `command` value from `REBOOT` to `FACTORY_RESET`. Keep the label,
interruption notice and `destructive` value unchanged. You are simulating a bad
adapter mapping in this local fixture.

Run the focused command assertion:

```bash
python3 -B -m unittest discover -s tests -p test_guard.py \
  -k test_confirmed_reboot_sends_exactly_one_reboot -v
```

The test must fail because the emitted command differs from `REBOOT`. An
assertion that checked only the chosen operation would miss this edit: the
selection still says `reboot`. Even the fixture’s `settings_kept` field follows
the unchanged `destructive` flag, so it cannot independently verify a router’s
actual settings.

| Check             | Original mapping | Deliberately broken mapping |
| ----------------- | ---------------- | --------------------------- |
| Chosen operation  | `reboot`         | `reboot`                    |
| Command mapping   | `REBOOT`         | `FACTORY_RESET`             |
| Command assertion | Passes           | Must fail                   |

If the mutated test stays green, inspect the file you edited and the reported
test count. Do not treat a run with no selected tests as evidence that the
mapping is correct.

## Inspect a reassuring check that misses the fault

Keep the deliberately broken mapping for this step. Python’s included debugger
can pause the test before it checks the returned command:

```bash
python3 -B -m pdb -m unittest discover -s tests -p test_guard.py \
  -k test_confirmed_reboot_sends_exactly_one_reboot -v
```

At the debugger’s `(Pdb)` prompt, pause at the operation-name assertion, after
the local write has returned. The line number is specific to this checkout.

```text
b tests/test_guard.py:183
c
```

Now `result` is the local write function’s return value. Inspect a deliberately
incomplete check that trusts only the operation name and settings flag:

```text
p result["confirmed_operation"] == "reboot" and result["settings_kept"] is True
```

It returns `True`. Check the command separately:

```text
p result["command_sent"]
p result["command_sent"] == "REBOOT"
```

The command is `FACTORY_RESET`; the comparison returns `False`. Enter `c` to
let the existing assertion fail, then `q` to leave the debugger. The debugger
can exit normally after a failed test; its own exit status is not a test verdict.

This incomplete comparison is constructed for the exercise. It is not a
pre-existing faulty test: the example already has the stronger command
assertion. The result explains why a success flag deserves scrutiny. The
simulation calculates `settings_kept` from its separate `destructive` flag;
it does not observe an actual router after applying the command.

| Observation after the deliberate edit                | Result          |
| ---------------------------------------------------- | --------------- |
| Operation name is reboot and settings flag says kept | `True`          |
| Command returned by the recorder                     | `FACTORY_RESET` |
| Command equals the expected reboot command           | `False`         |

Restore the fixture in this fresh checkout, then repeat the focused test:

```bash
git restore -- lib/fixtures/juniper.json
python3 -B -m unittest discover -s tests -p test_guard.py \
  -k test_confirmed_reboot_sends_exactly_one_reboot -v
git diff -- lib/fixtures/juniper.json
```

Expect a passing test and no diff for the fixture. Keep your real integration’s
equivalent command assertion when replacing the simulated service.

## Recorded offline result

The recorded check on 15 October 2026 used the pinned source above. The full
suite included its SDK-dependent assertions in an existing Rasa environment;
the focused restart exercise itself needs only Python.

| Run                        | Observed result                                       |
| -------------------------- | ----------------------------------------------------- |
| Full original suite        | 30 tests passed; none skipped                         |
| Restart checks shown above | Nine tests passed                                     |
| Changed command mapping    | One test failed on the command assertion, as intended |
| Restored original fixture  | The focused test passed again                         |

The deliberately broken version produced:

```text
AssertionError: 'FACTORY_RESET' != 'REBOOT'
```

The source snapshot was restored byte for byte. These runs called no model or
speech provider and sent no device command outside the local recorder. The saved test output records those local results; it does not establish a
real router’s behaviour.

## Keep confirmation and command checks separate

These tests construct a transcript containing the interruption notice and a
later customer reply. The local `boundary_confirmed` helper checks that order;
it does not understand whether the reply means yes. Rasa’s confirmation path
receives the model’s separate boolean decision.

Consequently, the direct Python tests do not establish that a model interprets
“no”, a changed device or a different requested operation correctly. Test those
turns through your agent before connecting a device. Record both the proposed
operation and the actual command, and check that cancellation sends nothing.

:::checkpoint{id="router-command-mapping" question="The customer confirmed a restart. The selection still says reboot, but the adapter’s command mapping was changed. Which observation identifies the fault?" options="The assistant says restart|The selected operation says reboot|The device command is FACTORY_RESET" answer="2"}

Choose the observation that reaches past the intended action to what the
adapter actually sent.

:::

:::solution{title="Check your reasoning"}
The command identifies the fault. The reply and selection describe the intended
operation; neither proves the adapter sent it. In this exercise the log is a
local recorder. With a real router, inspect the provider request and independent
device acknowledgement, then test any relevant settings afterwards.
:::

The local replay check also has a limit: its command history lives in memory.
It cannot prevent duplicate commands after a server restart or across several
workers. A production adapter needs durable operation identifiers and a way to
look up uncertain outcomes before retrying.

For the optional running Rasa agent, use the
[pinned setup guide](https://github.com/RasaHQ/rasa-community-resources/blob/4aa0c4419dc193fef7a969c12d59edcf720f2606/examples/mantle-text-telco-diagnostics-gpt/README.md).
That path installs dependencies and requires separate licence and model access.
The offline exercise above makes no claim about its chat accuracy, speech
quality or a real network’s recovery rate.

:::cta{href="/library/guides/mutation-test-an-agent-guard/" label="Test whether your own guard catches a deliberate fault"}
Choose one device-changing operation. Assert the service, command and operation
identifier at its adapter, then change the mapping deliberately. Keep the test
that catches that change before you replace the recorder with a live service.
:::