Add comprehensive rule documentation for AI-assisted development covering authentication surfaces, outbound-call safety invariants, and other project conventions to guide Claude's understanding of critical system behaviors.
2.3 KiB
description, paths
| description | paths | ||||
|---|---|---|---|---|---|
| emergency-number guard, concurrent-call cap, dial-path discipline — the outbound safety invariants |
|
Outbound-call safety
This is the safety core. A defect here means an unwanted real phone call, a runaway telephony bill, or — the one that matters most — an AI-initiated emergency call that should have been impossible.
-
is_emergency_number()is the single source of truth for refusal. It lives incore/dial_plan.py, blocks911/9911/112and their E.164 mappings (_BLOCKED= keys ∪ values), and normalises the input (strips spaces, dashes, dots) before comparing. If you learn of another dialled form that reaches emergency services, add it toEMERGENCY_NUMBERS— never work around the guard. -
Every outbound path goes through
gateway.make_call, and the guard is its first check — before the concurrency cap, beforecreate_call, before any SIP action. RESTmake_call, MCPmake_call, receptionist ring-back, and any transfer to an external number must funnel through it. Do not introduce a dial path that reachessip_engine.make_callwithout passing the guard first. If a new feature needs to place a call, it callsgateway.make_call. -
The concurrency cap is spend control, not decoration.
max_concurrent_calls(default 4) is checked inmake_callafter the emergency guard and before call creation, usinglen(call_manager.active_calls). Keep the ordering: refuse-emergency, then cap, then create. Don't move the count to after creation (it would off-by-one) and don't remove it. -
Refusals raise
ValueErrorat the gateway; surfaces translate it.make_callraisesValueErrorfor both refusals; the MCP tool converts it toToolError, and the REST layer maps it to a 4xx. Keep refusals as exceptions from the gateway — a refused call must never look like a placed one. -
The README's
[!CAUTION]block is a contract, not decoration. If you change refusal behaviour, the README caution and this rule must stay true. A system that quietly stops refusing emergency numbers is a serious regression even if every test still passes — add/keep a test that asserts each blocked form is refused.