Implements an unbreakable hard interrupt for agentic systems. **Core module (nexus/stop_protocol.py):** - Pre-tool-check gate: blocks tool execution when a stop is active (raises StopInterrupt) - STOP_ACK logging: append-only JSONL at ~/.hermes/stop_ack_log.jsonl - Hands-off registry: time-bounded locks at ~/.hermes/hands_off_registry.json (default 24h, auto-expires) - Full stop: atomic ack + hands-off in one call - Graceful handling of corrupted registry (starts empty, does not crash) **Compliance tests (tests/test_stop_protocol.py):** - 20 tests covering pre-tool-check gate, STOP_ACK logging, hands-off registry, full stop, edge cases, and invalid-state recovery - 100% compliance verification: test_all_stop_paths_covered **Documentation (docs/stop-protocol.md):** - Usage examples, component descriptions, and local test command Closes #844
65 lines
1.5 KiB
Markdown
65 lines
1.5 KiB
Markdown
# M1: The Stop Protocol
|
|
|
|
Refs: the-nexus #844, Epic #842
|
|
|
|
## Purpose
|
|
|
|
Make `Stop` an unbreakable hard interrupt for all agentic systems in the fleet.
|
|
|
|
## Components
|
|
|
|
### 1. Pre-tool-check gate
|
|
|
|
Every tool execution must pass through `StopProtocol.pre_tool_check(session_id)`.
|
|
If a stop command is active for that session, `StopInterrupt` is raised and the
|
|
tool call is blocked.
|
|
|
|
### 2. STOP_ACK logging
|
|
|
|
Every stop command is durably logged to `~/.hermes/stop_ack_log.jsonl` with:
|
|
- timestamp (ISO-8601 UTC)
|
|
- source (who issued the stop)
|
|
- session_id
|
|
- reason
|
|
|
|
The log is append-only and readable via `read_ack_log()`.
|
|
|
|
### 3. Hands-off registry
|
|
|
|
Stopped entities are added to `~/.hermes/hands_off_registry.json` with a
|
|
time-bounded lock (default 24 hours). Any attempt to modify a hands-off entity
|
|
must first check `is_hands_off(entity)`.
|
|
|
|
The registry auto-expires entries when their lock time passes.
|
|
|
|
### 4. Full stop
|
|
|
|
`full_stop(session_id, entity)` combines ack + hands-off in one atomic call.
|
|
|
|
## Usage
|
|
|
|
```python
|
|
from nexus.stop_protocol import StopProtocol
|
|
|
|
sp = StopProtocol()
|
|
|
|
# Before every tool call:
|
|
sp.pre_tool_check(session_id="sess_123")
|
|
|
|
# When user says "Stop":
|
|
sp.full_stop(session_id="sess_123", entity="ezra", reason="explicit halt")
|
|
|
|
# Before modifying any system:
|
|
if sp.is_hands_off("ezra"):
|
|
raise HandsOffError("ezra is locked — do not touch.")
|
|
```
|
|
|
|
## Compliance
|
|
|
|
100% test coverage enforced by `tests/test_stop_protocol.py` (20 tests).
|
|
Run locally:
|
|
|
|
```bash
|
|
python3 -m pytest tests/test_stop_protocol.py -v
|
|
```
|