microsoft / microsoft/agent-governance-toolkit

[Bug]: Tutorial 14 and test_total_handoffs_always_zero assert an obsolete no-handoff contract

Open Beginner friendly
#3,742 1 comment 0 reactions 0 assignees View on GitHub
needs-review:MEDIUM
Dominant language
Python
Stars
6.3k
Forks
1.1k
Avg merge
5d 11h
Merged PRs (30d)
142

Description

### Package

Documentation (also `agent-hypervisor`)

### Description

`docs/tutorials/14-kill-switch-and-rate-limiting.md` states:

> in the public preview, all in-flight saga steps are automatically compensated (rolled back) — there is no handoff to substitute agents

and that the total handoff count is *"always 0"*.

`main` implements handoff. `KillSwitch._find_substitute()` resolves a registered substitute, and `kill()` marks steps `HandoffStatus.HANDED_OFF` while incrementing `handoff_success_count`. Observed with a substitute registered: `['handed_off', 'handed_off']` and `handoff_success_count: 2`.

The obsolete contract also survives in the test suite:

```python
def test_total_handoffs_always_zero(self):
"""Public preview: handoffs not supported."""
ks = KillSwitch()
ks.kill("a1", "s1", KillReason.MANUAL, in_flight_steps=[{"step_id": "s", "saga_id": "x"}])
assert ks.total_handoffs == 0
```

This passes only because no substitute is registered, which makes the assertion vacuously true. Its name and docstring assert a contract the code has since abandoned, and `test_total_handoffs_counts_successes` in the same file asserts the opposite behaviour. As written it would not catch a regression that broke handoff.

### How does this impact your work?

Minor. I was evaluating the kill switch from the documentation first and concluded handoff was unavailable, which was wrong. Easy to recover from once reading the source, but the tutorial is the more likely first stop.

### Timeline

No hard deadline.

### Steps to Reproduce

```python
from hypervisor.security.kill_switch import KillSwitch, KillReason

ks = KillSwitch()
ks.register_agent("did:agentmesh:primary", lambda: None)
ks.register_substitute("s6", "did:agentmesh:substitute")
r = ks.kill("did:agentmesh:primary", "s6", KillReason.QUARANTINE_TIMEOUT,
in_flight_steps=[{"step_id": "st1", "saga_id": "saga1"}])

print([h.status.value for h in r.handoffs]) # ['handed_off']
print(r.handoff_success_count) # 1
print(ks.total_handoffs) # 1
```

### Environment

- `agent-hypervisor/src/hypervisor/security/kill_switch.py` @ `c497c6f5` (2026-06-17)
- `docs/tutorials/14-kill-switch-and-rate-limiting.md` @ `main`
- Python 3.14.6, Windows 11

### Suggested fix

Update tutorial 14 to describe handoff as implemented, with compensation as the fallback when no substitute is registered. Rename the test to `test_total_handoffs_zero_without_substitute` and correct its docstring so the assertion documents the condition it actually tests.

### Related

- #3739 — kill switch behaviour is likewise absent from `LIMITATIONS.md`.

---

*Code of Conduct: I agree to follow the Microsoft Open Source Code of Conduct.*

Contributor guide

Open the contributing guide

Research direction

Start with docs/tutorials/14-kill-switch-and-rate-limiting.md and compare its handoff claims with KillSwitch behavior described in the issue. Locate test_total_handoffs_always_zero alongside test_total_handoffs_counts_successes, then update the tutorial and test naming/docstring so handoff is documented and the no-substitute case is explicit.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
documentation, security, testing
Issue type
Documentation
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.