Skip to main content

Component map

What this page is

The architecture-index post-build plugin at docusaurus/ratiba/plugins/architecture-index.js walks every markdown file under docusaurus/ratiba/docs/, parses the components:, adrs:, and runbook_steps: frontmatter, and emits a structured inventory at docs/architecture-index.json. That JSON is the machine view of the codebase — a coverage table, a dependency adjacency list, a stale-pointer warning queue. The plugin contract is fully described in ADR-0011.

This page is the human view of the same territory. It sits one rung higher than the JSON: a folder-by-folder tour of backend/app/, the module-level import topology drawn as a graph, and a short prose explanation of what the plugin actually checks. Read it when you need to know where a behaviour lives before going to the per-feature explainer at How it works or the cross-cutting deep page at Architecture / System overview.

Two recent additions worth flagging before the full tour:

  • Phase 0 knowledge layer (app/services/knowledge.py + app/services/inquiry.py + app/services/answer_shaper.py): the "no-RAG RAG" design that injects per-tenant knowledge snippets into the answer_shaper user template without touching the system prompt. Behavioural deep-dive at Knowledge answers; architectural decision at ADR-0013.

  • Voice full-duplex primitives (app/voice/config.py, barge_in.py, backchannel.py, hard_interrupt.py, listening_ack.py, voice_speed.py, brain_stream.py): the M7 voice channel upgraded to support natural turn-taking (barge-in, adaptive speed, bilingual hard-interrupt, agentic streaming seam). Behavioural deep-dive at Voice conversation.

The companion C4 view lives at Architecture overview; the data flows live at Architecture / Data flow; the schema shape lives at Architecture / Schema evolution. This page is the "where is the code" reference.


app/ folder tour

Every top-level package in backend/app/ has a single, narrow purpose. The table below names that purpose and lists the modules a new contributor would open first. Verify against on-disk state with ls backend/app/<folder>/ — drift between this table and reality is itself a stale-pointer warning the plugin will eventually catch (last_verified vs. component file mtimes, per ADR-0011 D6).

FolderPurposeKey modules
app/api/FastAPI routers — webhooks (Meta + payments), admin endpoints, channel WebSocketswebhooks/whatsapp.py, webhooks/instagram.py, webhooks/messenger.py, webhooks/daraja.py, webhooks/pesapal.py, admin/chat_ws.py, admin/tenants.py, admin/catalog.py, admin/personality.py, channels/web.py, dependencies/admin_auth.py, dependencies/tenant.py, health.py
app/channels/Channel substrate + 5 adapters + 1 SMS sink_base/protocol.py (Channel + ChannelKind + Tier), _base/registry.py, _meta_messaging/, whatsapp/_channel.py + sender.py + models.py, voice/, web/, instagram/, messenger/
app/orchestrator/Conversation FSMs (LangGraph)booking_graph.py, cancel_graph.py, reschedule_graph.py, state.py (FSM_GRAPH_VERSION + BookingState), dispatcher.py, threads.py, handoff.py
app/services/Domain services — pure business logic, no I/O glue. Includes Phase 0 knowledge layer (knowledge.py + inquiry.py + answer_shaper.py) — see Knowledge answers.availability.py, cross_sell.py, reservations.py, personality_config.py, catalog_importer.py, csv_extractor.py, vision_extractor.py, relation_inferrer.py, answer_shaper.py, inquiry.py, knowledge.py, channel_switch.py
app/llm/LLM router + provider adapters + cost ledgerrouter.py, cost.py, _response.py, adapters/anthropic.py, adapters/openai.py, role_assignments.yaml
app/payments/M-Pesa STK push + PesaPal cards + cancellation reversaldaraja.py, initiate_daraja.py, poll_daraja.py, pesapal.py, pesapal_flow.py, callbacks.py, cancel_reversal.py, credentials.py, state.py
app/agents/Identity-resolution + agent abstractionsidentity_resolver.py
app/admin/Admin orchestrator (4-state shallow FSM, dual-channel rail)orchestrator.py, commands.py, nl_router.py, message_router.py, stats.py, briefing.py, fanout.py
app/persistence/DB pools + DAOs + LangGraph saver shimsession.py, pool.py, customers.py, personality_config.py, catalog_imports.py, tenant_scoped_saver.py, redis.py, encryption.py, onboarding_psycopg.py
app/tenancy/Multi-tenant infrastructure + onboarding lifecyclecontext.py (current_tenant ContextVar), models.py (Tenant ORM), schemas.py, onboarding.py, lifecycle.py, keycloak_admin.py, handoff_defaults.py
app/prompts/Prompt YAML files + loaderloader.py (Prompt model + render), intent_classifier.yaml, answer_shaper.yaml, booking_*.yaml, cancel_request.yaml, reschedule_request.yaml, cross_sell_offer_en.yaml, cross_sell_offer_sw.yaml, personality_defaults.yaml, handoff_summarizer.yaml, vision_catalog_extract.yaml, relation_inference.yaml, catalog_gap_fill.yaml, admin_nl_routing.yaml
app/eval/Calibration runner + scenario loader + metricsscenario.py, runner.py, admin_runner.py, admin_scenario.py, metrics.py, langfuse_sink.py, __main__.py
app/voice/Voice-channel internals (LiveKit + Deepgram + ElevenLabs glue) + full-duplex turn-taking primitives. See Voice conversation.agent.py (LiveKit AgentSession plugin), deepgram.py (Nova-3 STT), elevenlabs.py (Multilingual v2 TTS), eot.py (end-of-turn detector), filler_clock.py (filler-word pacing), handoff.py, payment_listener.py, config.py (VoiceConfig dials: barge-in threshold, WPM bounds, backchannel phrases), barge_in.py (customer speech interrupts bot TTS), backchannel.py (listening acknowledgements — "mm-hm", "ndiyo"), hard_interrupt.py (bilingual hard-stop: "stop"/"simama"), brain_stream.py (VoiceStreamEvent agentic streaming seam for future agentic backend), listening_ack.py (visual/audio cue while awaiting FSM), voice_speed.py (WPM-adaptive TTS speed control)
app/notifications/Outbound NotificationSinks (SMS + reminders)sms.py (Africa's Talking), reminders.py
app/workers/Background jobs — APScheduler in-processpayments_reaper.py

Top-level files (app/main.py, app/config.py, app/worker.py) carry process bootstrap — FastAPI app construction, settings loading via Pydantic, and APScheduler setup respectively.


Module-level dependency graph

The graph below shows the import topology between top-level packages inside backend/app/. Edges are directed A -> B whenever a module in A imports any symbol from B. The shape is acyclic by design — domain services never import from the orchestrator, the orchestrator never imports from API handlers, and persistence sits at the bottom of the stack with tenancy as its only downstream dependency.

Three properties of the graph worth calling out:

  1. No upstream/downstream violations. app/services/ never imports app/orchestrator/ — services are pure domain logic, the FSM orchestrates them. This holds for the Phase 0 knowledge layer too: knowledge.py and inquiry.py are called by the FSM node (via orchestrator --> services), not the other way around. app/persistence/ never imports app/services/ or anything above it — the bottom of the stack only knows about tenancy. app/prompts/ is a leaf (only loader + YAML files) and depends on nothing inside app/.
  2. app/tenancy/ is the universal substrate. Every package that touches the database eventually reads current_tenant.get() from app/tenancy/context.py. The contextvar propagation is what makes schema-per-tenant isolation work without explicit tenant arguments on every function (per ADR-0002). The per-tenant knowledge_snippets table is scoped identically — knowledge.py inherits the tenant search path via the same contextvar without any extra argument threading.
  3. The API layer is a thin shell. app/api/ only ever delegates downward — to channels (for webhook → adapter routing), to admin (for /admin/* endpoints), to orchestrator (for the rare direct FSM entry from REST). It owns no business logic.

architecture-index.json humanised

The plugin at docusaurus/ratiba/plugins/architecture-index.js runs as a Docusaurus postBuild step. On every npm run build it walks every .md / .mdx file under docusaurus/ratiba/docs/, parses the YAML frontmatter, and writes structured output to docs/architecture-index.json (gitignored at runtime, regenerated by every build). The five behaviours that matter for this page:

  • components: scan + component_map inversion. Every page declares which app/<...> files it documents. The plugin inverts the map — for every component, which pages mention it. Multi-page components (e.g. app/orchestrator/booking_graph.py shows up in Architecture overview, How it works / Conversation FSM, How it works / Cross-sell, How it works / Payments, Quick start / First booking) are valuable signal: they're load-bearing modules that deserve careful change control.
  • Coverage threshold ≥ 80% per ADR-0011 D6. The plugin enumerates every Python file under backend/app/services/ and backend/app/agents/ and reports the percentage referenced by at least one components: list. The current threshold is 80% — pages added during M12 W2 + W3 lift the count from the M11 baseline; below 80% the build emits a warning. The latest build reports 64% — the remaining gap closes as Wave 3 lands the Architecture deep pages.
  • Stale last_verified flagging. Every page carries a last_verified: YYYY-MM-DD field. The plugin checks the most-recent git log timestamp of every file in the page's components: list; if any component has been modified more recently than 30 days before last_verified, the page is flagged in stale_pointer_warnings. Per ADR-0011 D6, git-log results are cached within the build pass — a component referenced by multiple pages reuses the cached lookup.
  • ADR cross-validation. Every page's adrs: [N, ...] list is validated against docs/adr/ADR-NNNN-*.md existence on disk. Fat-fingered numbers or renumbered ADRs fail the build with a clear error.
  • runbook_steps aggregation. Every "How it works" leaf carries a 3-step runbook_steps: list (the "Try this on local dev" block). The plugin collects these into runbook_steps_by_page for future consolidated-runbook tooling. M12 doesn't ship that view; the data is captured for M13+.

The plugin output lives at docs/architecture-index.json and is gitignored — every build regenerates it deterministically. The frontmatter validator at docusaurus/ratiba/scripts/validate-frontmatter.js runs on npm run validate-frontmatter (and as prebuild) and enforces that every page in architecture/, how-it-works/, quickstart/, methodology/, and runbook/ has the three required fields (components:, adrs:, runbook_steps:) — empty lists are valid, missing fields fail.


Architecture peers:

How it works leaves (each leaf names the modules in this map):

Decisions: