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.9 KiB
description, paths
| description | paths | |||
|---|---|---|---|---|
| composition-root lifespan, nested MCP http_app lifespan, app.state wiring, route/mount ordering, honest /health |
|
Lifespan, composition root & route ordering
main.py's lifespan is the composition root: it builds the
gateway and every service, wires them by constructor/registration, and hangs the
long-lived ones on app.state. This is the one place dependencies are
assembled.
-
Build services here, inject them — nothing self-constructs its deps. The gateway, classifier, transcription, TTS, routing, recording, receptionist, and notification services are all constructed in the lifespan and wired together (e.g. the receptionist receives tts/transcription/recording/routing;
launch_hold_slayeris registered as theHOLD_SLAYERmode handler). A new service is built here and passed in, not instantiated deep in a call path. -
The MCP sub-app's lifespan MUST be nested. The lifespan opens
async with mcp_http_app.lifespan(app):around all startup. FastMCP's streamable-HTTP session manager is initialised inside its lifespan; mount the app without entering that context and every/mcprequest 500s ("session manager not initialised" / "Task group is not initialized"). Keep the nesting. This is the same landmine across the estate's mounted-MCP services. -
app.stateis the handoff to request handlers. The lifespan setsapp.state.gateway,.routing_service,.transcription_service,.notification_service,.recording_service. Dependencies in api/deps.py read these and raise503if not yet set. MCP tools reach the gateway via the lazy_get_gateway_instanceresolver. Don't reach for module-level globals; go throughapp.state. -
Route/mount registration order is load-bearing:
call_historyrouter registers beforecalls— both live under/api/v1/calls, andcalls'GET /{call_id}would otherwise swallow the literal pathhistory. Keep history first.- The
"/mcp"mount and all API/WS/health routes register before the"/"static dashboard mount — a root mount matches every path, so anything after it is unreachable. The dashboard mount stays last, and only whendashboard/build/exists.
-
/healthis honest by construction.healthy= real (non-MockSIPEngine) engine and registered trunk and reachable DB; it also reports STT/TTS last-known reachability via_availability. Don't relax any of these to make a probe pass — a degraded gateway must read asdegraded, with the reason visible. -
Shutdown reverses startup. Stop notifications, stop the gateway (which cancels tracked tasks, ends active calls, stops SIP then media), close the DB. New long-lived resources get a matching teardown here — don't leak a task or a client across restarts.