diff --git a/docs/diagrams/README.md b/docs/diagrams/README.md
new file mode 100644
index 0000000000..9912a4ddd4
--- /dev/null
+++ b/docs/diagrams/README.md
@@ -0,0 +1,48 @@
+# Diagrams
+
+Mermaid sources (`.mmd`) and exported SVGs for OmniRoute v3.8.0 architecture flows.
+
+## Canonical diagrams
+
+| Source | Exported | Used in |
+| -------------------------------------------------- | ---------------------------------------- | ---------------------------------------------------- |
+| [request-pipeline.mmd](./request-pipeline.mmd) | [SVG](./exported/request-pipeline.svg) | docs/ARCHITECTURE.md, docs/CODEBASE_DOCUMENTATION.md |
+| [auto-combo-9factor.mmd](./auto-combo-9factor.mmd) | [SVG](./exported/auto-combo-9factor.svg) | docs/AUTO-COMBO.md |
+| [resilience-3layers.mmd](./resilience-3layers.mmd) | [SVG](./exported/resilience-3layers.svg) | docs/RESILIENCE_GUIDE.md, CLAUDE.md |
+| [i18n-flow.mmd](./i18n-flow.mmd) | [SVG](./exported/i18n-flow.svg) | docs/I18N.md |
+| [mcp-tools-37.mmd](./mcp-tools-37.mmd) | [SVG](./exported/mcp-tools-37.svg) | docs/MCP-SERVER.md |
+| [cloud-agent-flow.mmd](./cloud-agent-flow.mmd) | [SVG](./exported/cloud-agent-flow.svg) | docs/CLOUD_AGENT.md |
+| [authz-pipeline.mmd](./authz-pipeline.mmd) | [SVG](./exported/authz-pipeline.svg) | docs/AUTHZ_GUIDE.md |
+| [db-schema-overview.mmd](./db-schema-overview.mmd) | [SVG](./exported/db-schema-overview.svg) | docs/CODEBASE_DOCUMENTATION.md |
+
+## How to update
+
+1. Edit `*.mmd`.
+2. Re-render: `npm run docs:render-diagrams` (uses `@mermaid-js/mermaid-cli`).
+3. Commit both `.mmd` and `.svg`.
+
+If `@mermaid-js/mermaid-cli` is not available locally, install it once:
+
+```bash
+npm install -g @mermaid-js/mermaid-cli
+```
+
+The script renders every `.mmd` in `docs/diagrams/` into `docs/diagrams/exported/*.svg`
+with a white background, suitable for both dark and light themes.
+
+## Linking from a doc
+
+```markdown
+
+
+> Source: [diagrams/request-pipeline.mmd](./diagrams/request-pipeline.mmd)
+```
+
+## Conventions
+
+- One concept per diagram. Don't try to fit the whole platform in one chart.
+- Keep node labels short (3–6 words). Use `
` for line breaks inside nodes.
+- Prefer `flowchart LR` for pipelines and `flowchart TB` for layered models.
+- Use `sequenceDiagram` for interactive (request/response) flows.
+- Use `erDiagram` for database schema overviews.
+- Update both `.mmd` and `.svg` in the same commit. Keep them in lock-step.
diff --git a/docs/diagrams/authz-pipeline.mmd b/docs/diagrams/authz-pipeline.mmd
new file mode 100644
index 0000000000..78b9b5b09c
--- /dev/null
+++ b/docs/diagrams/authz-pipeline.mmd
@@ -0,0 +1,23 @@
+%% AuthZ pipeline (3 route classes + policy evaluation)
+%% Reflects: src/middleware.ts, src/server/authz/pipeline.ts,
+%% src/server/authz/policies/{public,clientApi,management}.ts
+%% v3.8.0
+flowchart LR
+ Req["Incoming request"] --> Strip["Strip trusted internal headers
(x-omniroute-auth-*)"]
+ Strip --> Classify["classifyRoute()"]
+ Classify -->|PUBLIC| PubP["publicPolicy
(login, health, status)"]
+ Classify -->|CLIENT_API| CliP["clientApiPolicy
(/api/v1/*, /v1/*)"]
+ Classify -->|MANAGEMENT| MgmtP["managementPolicy
(dashboard, settings, admin)"]
+
+ PubP -->|allow| Stamp["Stamp x-omniroute-auth-*
(kind / id / label / scopes)"]
+ CliP -->|extract Bearer| Validate["validateApiKey()"]
+ Validate -->|valid| Stamp
+ Validate -->|"invalid (REQUIRE_API_KEY=true)"| Reject401["401 AUTH_002"]
+ Validate -->|"absent (REQUIRE_API_KEY=false)"| Anon["anonymous fallthrough"] --> Stamp
+
+ MgmtP -->|session ok| Stamp
+ MgmtP -->|Bearer w/ manage scope| Stamp
+ MgmtP -->|"Bearer invalid"| Reject403["403 AUTH_001"]
+ MgmtP -->|no auth| Reject401Mgmt["401 / 302 /login"]
+
+ Stamp --> Handler["Handler
(uses assertAuth)"]
diff --git a/docs/diagrams/auto-combo-9factor.mmd b/docs/diagrams/auto-combo-9factor.mmd
new file mode 100644
index 0000000000..7b604b43d3
--- /dev/null
+++ b/docs/diagrams/auto-combo-9factor.mmd
@@ -0,0 +1,23 @@
+%% Auto-Combo 9-factor scoring
+%% Reflects: open-sse/services/autoCombo/scoring.ts (DEFAULT_WEIGHTS, sum = 1.0)
+%% v3.8.0
+flowchart TB
+ Request["Incoming request"] --> Candidates["Eligible candidates
(provider × model × account)"]
+ Candidates --> Score["Compute composite score
per candidate"]
+
+ subgraph Factors["9-factor scoring weights (sum = 1.0)"]
+ f1["health (0.22)"]
+ f2["quota (0.17)"]
+ f3["costInv (0.17)"]
+ f4["latencyInv (0.13)"]
+ f5["taskFit (0.08)"]
+ f6["specificityMatch (0.08)"]
+ f7["stability (0.05)"]
+ f8["tierPriority (0.05)"]
+ f9["tierAffinity (0.05)"]
+ end
+
+ Score --> Factors
+ Factors --> Final["Sort by score
(desc)"]
+ Final --> Top["Pick top-N targets"]
+ Top --> Dispatch["Dispatch sequentially
(short-circuit on success)"]
diff --git a/docs/diagrams/cloud-agent-flow.mmd b/docs/diagrams/cloud-agent-flow.mmd
new file mode 100644
index 0000000000..32b5a9cea6
--- /dev/null
+++ b/docs/diagrams/cloud-agent-flow.mmd
@@ -0,0 +1,32 @@
+%% Cloud Agent task lifecycle
+%% Reflects: src/lib/cloudAgent/* and src/app/api/v1/agents/tasks/*
+%% Supported agents: codex-cloud, devin, jules
+%% v3.8.0
+sequenceDiagram
+ participant U as User
+ participant API as /v1/agents/tasks
+ participant Reg as Cloud Agent Registry
+ participant Ag as Cloud Agent
(codex-cloud / devin / jules)
+ participant DB as SQLite domain DB
+
+ U->>API: POST createTask (description, sources)
+ API->>API: Zod validation + AuthZ (management)
+ API->>Reg: getAgent(providerId)
+ Reg->>Ag: instantiate with credentials
+ Ag->>Ag: createTask() returns planning
+ Ag-->>API: taskId + status: planning
+ API->>DB: insertCloudAgentTask
+ API-->>U: taskId
+
+ U->>API: GET /tasks/:id (poll)
+ API->>Ag: getStatus(externalId)
+ Ag-->>API: status, plan, messages
+ API->>DB: updateCloudAgentTask
+ API-->>U: snapshot
+
+ U->>API: POST approvePlan
+ API->>Ag: approvePlan(externalId)
+ Ag-->>API: status: running
+ Note over Ag: Async execution upstream
+
+ Ag-->>DB: stream events / final result
diff --git a/docs/diagrams/db-schema-overview.mmd b/docs/diagrams/db-schema-overview.mmd
new file mode 100644
index 0000000000..debf71f3b1
--- /dev/null
+++ b/docs/diagrams/db-schema-overview.mmd
@@ -0,0 +1,16 @@
+%% Database schema overview (selected core tables)
+%% Reflects: src/lib/db/* (45+ modules, 55 migrations)
+%% v3.8.0
+erDiagram
+ api_keys ||--o{ api_key_usage : tracks
+ api_keys ||--o{ rate_limits : enforces
+ providers ||--o{ provider_connections : holds
+ provider_connections ||--o{ domain_circuit_breakers : protected_by
+ combos ||--o{ combo_targets : contains
+ combos ||--o{ combo_executions : logs
+ spend_buffer ||--o{ usage_aggregations : flushed_to
+ memory_documents ||--o{ memory_chunks : split_into
+ skills_runs ||--o{ skills_logs : produced
+ agent_tasks ||--o{ agent_task_events : streams
+ audit_log }|--|| api_keys : by
+ mcp_audit }|--|| api_keys : by
diff --git a/docs/diagrams/i18n-flow.mmd b/docs/diagrams/i18n-flow.mmd
new file mode 100644
index 0000000000..50ddb293c9
--- /dev/null
+++ b/docs/diagrams/i18n-flow.mmd
@@ -0,0 +1,14 @@
+%% Incremental hash-based i18n pipeline
+%% Reflects: scripts/i18n/* and docs/i18n//*.md mirror layout
+%% v3.8.0
+flowchart LR
+ Src["Source MDs
(CLAUDE.md, docs/**/*.md)"] --> Hash["sha256 hash"]
+ Hash --> State[".i18n-state.json"]
+ State -->|diff| Dirty["Mark source dirty"]
+ Dirty --> Loop{"For each locale
(39 langs)"}
+ Loop --> LLM["OmniRoute
/chat/completions
(cx/gpt-5.4-mini)"]
+ LLM --> Target["Write
docs/i18n/<locale>/<rel-path>.md"]
+ Target --> State
+ Target --> CI{"CI drift check
(npm run i18n:check)"}
+ CI -->|in sync| Pass["pass"]
+ CI -->|drift| Fail["fail
(exit 1)"]
diff --git a/docs/diagrams/mcp-tools-37.mmd b/docs/diagrams/mcp-tools-37.mmd
new file mode 100644
index 0000000000..bd0afb082c
--- /dev/null
+++ b/docs/diagrams/mcp-tools-37.mmd
@@ -0,0 +1,23 @@
+%% MCP Server tool inventory by category
+%% Reflects: open-sse/mcp-server/tools/* and docs/MCP-SERVER.md
+%% v3.8.0
+flowchart LR
+ MCP["MCP Server
37 tools total"] --> Core["Core (30)"]
+ MCP --> Mem["Memory (3)"]
+ MCP --> Skl["Skills (4)"]
+
+ Core --> Essential["Essential (8)
get_health, list_combos,
switch_combo, check_quota,
route_request, cost_report,
list_models_catalog,
get_combo_metrics"]
+ Core --> Search["Search (1)
web_search"]
+ Core --> Advanced["Advanced (11)
simulate_route, set_budget_guard,
set_routing_strategy,
set_resilience_profile,
test_combo, get_provider_metrics,
best_combo_for_task, explain_route,
get_session_snapshot,
db_health_check, sync_pricing"]
+ Core --> Cache["Cache (2)"]
+ Core --> Compression["Compression (5)"]
+ Core --> Tunnels["1Proxy / Tunnels (3)"]
+
+ Mem --> M1["memory_search"]
+ Mem --> M2["memory_save"]
+ Mem --> M3["memory_delete"]
+
+ Skl --> S1["skill_invoke"]
+ Skl --> S2["skill_list"]
+ Skl --> S3["skill_diagnose"]
+ Skl --> S4["skill_uninstall"]
diff --git a/docs/diagrams/request-pipeline.mmd b/docs/diagrams/request-pipeline.mmd
new file mode 100644
index 0000000000..d0883aa76c
--- /dev/null
+++ b/docs/diagrams/request-pipeline.mmd
@@ -0,0 +1,24 @@
+%% Request pipeline for /v1/chat/completions
+%% Reflects: src/app/api/v1/chat/completions/route.ts → open-sse/handlers/chatCore.ts
+%% v3.8.0
+flowchart LR
+ Client["Client
(IDE/CLI/SDK)"] --> Route["Next.js Route
/v1/chat/completions"]
+ Route --> CORS["CORS preflight"]
+ CORS --> Validate["Zod validation
(request body)"]
+ Validate --> Authz["AuthZ pipeline
(extractApiKey +
isValidApiKey)"]
+ Authz --> Policy["API key policy
(allowlist + scopes)"]
+ Policy --> Guard["Prompt-injection
guardrail"]
+ Guard --> ChatCore["handleChatCore"]
+ ChatCore --> Cache{"Cache hit?"}
+ Cache -->|yes| Return["Return cached"]
+ Cache -->|no| Rate["Rate limit
(per-key, per-IP)"]
+ Rate --> Combo{"Combo
target?"}
+ Combo -->|combo| Resolve["resolveComboTargets
(14 strategies)"]
+ Resolve --> Single["handleSingleModel
(per target)"]
+ Combo -->|single| Single
+ Single --> Translate["translateRequest
(OpenAI↔Claude↔Gemini)"]
+ Translate --> Exec["getExecutor
(31 executors)"]
+ Exec --> Upstream["Upstream Provider
(177 providers)"]
+ Upstream --> Stream["SSE / JSON"]
+ Stream --> Transformer["responsesTransformer
(Responses↔Chat)"]
+ Transformer --> Client
diff --git a/docs/diagrams/resilience-3layers.mmd b/docs/diagrams/resilience-3layers.mmd
new file mode 100644
index 0000000000..777198946a
--- /dev/null
+++ b/docs/diagrams/resilience-3layers.mmd
@@ -0,0 +1,21 @@
+%% 3-layer resilience model
+%% Reflects: src/shared/utils/circuitBreaker.ts, src/sse/services/auth.ts,
+%% open-sse/services/accountFallback.ts
+%% v3.8.0
+flowchart TB
+ Req["Request"] --> L1{"Provider Circuit Breaker
(scope: whole provider)"}
+ L1 -->|CLOSED| L2
+ L1 -->|OPEN| Block1["Skip provider
(or 503 retry-after)"]
+ L1 -->|HALF_OPEN| Probe["Allow probe"]
+ Probe --> L2
+
+ L2{"Connection Cooldown
(scope: one account/key)"} -->|available| L3
+ L2 -->|cooling down| Skip2["Skip this connection
fall back to next"]
+
+ L3{"Model Lockout
(scope: provider × conn × model)"} -->|allowed| Exec["Execute upstream"]
+ L3 -->|locked| Skip3["Fall back to next model"]
+
+ Exec -->|success| Clear["Clear cooldowns/lockouts"]
+ Exec -->|"408 / 500 / 502 / 503 / 504"| TripL1["Trip provider breaker"]
+ Exec -->|"401 / 403 (account)"| TripL2["Start connection cooldown"]
+ Exec -->|"429 quota (model)"| TripL3["Start model lockout"]