diff --git a/.agents/skills/review-agents-md/SKILL.md b/.agents/skills/review-agents-md/SKILL.md new file mode 100644 index 0000000..ecb703e --- /dev/null +++ b/.agents/skills/review-agents-md/SKILL.md @@ -0,0 +1,206 @@ +--- +name: review-agents-md +description: Audit Dograh `AGENTS.md` files for drift against the live repo and for bad scope boundaries between parent and child docs. Use when the user asks to review existing AGENTS files, identify stale guidance, decide whether a subtree needs its own `AGENTS.md`, or update the `AGENTS.md` hierarchy under the repo root, `api/`, or `ui/`. +--- + +# Review AGENTS.md + +Audit first. Report drift, missing coverage, and wrong ownership boundaries before editing docs unless the user explicitly asks for patches. + +## Freshness Rule + +Treat the repo as source of truth. + +- Trust current code and current directory layout over any `AGENTS.md`, `README.md`, or this skill's references. +- If prose and code disagree, report the prose as stale. +- If a reference file in this skill disagrees with the repo, trust the repo and mention the drift. + +## Workflow + +### 0. Refresh the seam reference before using it + +If subagents are available, refresh `references/dograh-seams.md` before relying on it. + +- Spawn exactly one subagent for this maintenance pass. +- Tell the subagent to inspect the live repo. +- Limit its patch set to `.agents/skills/review-agents-md/references/dograh-seams.md`. +- Also allow `.agents/skills/review-agents-md/scripts/inventory_agents_md.py`, but only if the helper itself needs a repo-specific fix. +- Tell the subagent not to recurse into this same seam-refresh workflow. This is a one-level maintenance pass, not an infinite self-audit loop. +- Tell the subagent not to review or patch any repo `AGENTS.md` files yet. Its job is only to refresh the seam reference and helper. +- After the subagent returns, review its diff quickly before using `dograh-seams.md` in the main audit. + +Use a prompt shaped like: + +```text +Review and refresh .agents/skills/review-agents-md/references/dograh-seams.md against the live Dograh repo. Patch only that file, and patch .agents/skills/review-agents-md/scripts/inventory_agents_md.py only if needed. Do not recurse into another seam-refresh pass. Do not review or edit any AGENTS.md files yet. +``` + +If subagents are not available, do the same seam refresh locally before continuing. + +### 1. Inventory the current hierarchy + +Run the helper first from the repo root: + +```bash +python .agents/skills/review-agents-md/scripts/inventory_agents_md.py +``` + +This prints: + +- every discovered `AGENTS.md` +- child `AGENTS.md` ownership boundaries +- immediate child directories for each scope +- large uncovered subtrees that may deserve their own `AGENTS.md` + +Then confirm with direct file discovery when needed: + +```bash +rg --files -g 'AGENTS.md' . +find api ui -name AGENTS.md | sort +``` + +### 2. Read top-down before judging details + +Read in this order: + +1. repo root `AGENTS.md` +2. `api/AGENTS.md` +3. `ui/AGENTS.md` +4. deeper `AGENTS.md` files under those trees + +For each file, write a one-line ownership statement in your notes: + +- what subtree it owns +- what shared rules it should contain +- which deeper docs, if any, should own implementation details instead + +### 3. Verify each doc against the live code + +Check directory trees, route aggregators, registration points, and extension seams instead of relying on filenames mentioned in prose. + +Dograh files worth checking early: + +- `api/routes/main.py` +- `api/routes/telephony.py` +- `api/services/integrations/loader.py` +- `api/services/integrations/registry.py` +- `api/services/pipecat/run_pipeline.py` +- `api/tasks/run_integrations.py` +- `api/services/telephony/registry.py` +- `api/services/telephony/factory.py` +- `api/services/telephony/providers/__init__.py` +- `ui/src/app/` +- `ui/src/components/` +- `ui/src/lib/auth/` +- `ui/src/client/` + +Read [dograh-seams.md](references/dograh-seams.md) when you need a fast repo-specific starting map. + +### 4. Apply the hierarchy tests + +Use these tests for every scope: + +- `parent-fit`: The parent doc explains immediate child systems, shared invariants, and navigation for the subtree it owns. +- `child-fit`: A deeper doc owns local extension contracts, module-specific gotchas, and file-level patterns for its own subtree. +- `no-duplication`: The parent does not restate detailed child implementation guidance that should live in the child doc. +- `downward-pointing`: The doc should point contributors toward the next relevant subdirectory or deeper `AGENTS.md` instead of trying to explain the whole subtree itself. +- `no-gaps`: If a large or extension-heavy subtree has rules the parent cannot explain cleanly in a few lines, flag a missing child `AGENTS.md`. +- `no-drift`: File trees, commands, extension points, and architecture claims still match the code. + +### 5. Dograh-specific review heuristics + +#### Root `AGENTS.md` + +Expect root to stay high-level. + +- It should describe the top-level project shape, shared stack, and shared local-development expectations. +- It should mention top-level applications and support directories that matter to contributors. +- It should not try to document backend-internal extension contracts or frontend component internals. +- If a top-level directory materially matters to contributors and is missing from root guidance, report `missing-parent-coverage`. + +#### `api/AGENTS.md` + +Expect `api/AGENTS.md` to orient a contributor across backend domains, not to document every local contract in full. + +- It should point to where routes, services, DB access, schemas, tasks, tests, and security invariants live. +- It should accurately describe where workflow execution lives. In current Dograh, that spans `api/services/workflow/`, `api/services/pipecat/`, and post-call work in `api/tasks/run_integrations.py`. +- It should accurately describe telephony as a substantial subsystem, not just as one route file. +- It should mention integration extensibility and defer package-level rules to `api/services/integrations/AGENTS.md`. +- If `api/services/telephony/` or `api/services/workflow/` are complex enough that the parent doc becomes vague or overloaded, report `missing-child-agents`. + +#### `api/services/integrations/AGENTS.md` + +Expect this file to own the integration package contract. + +- It should explain package registration, node model/spec patterns, runtime collection, completion handlers, optional routes, import discipline, and testing expectations. +- It should match the live registry/loader path instead of describing central manual wiring that no longer exists. +- It should not require edits to `workflow/dto.py`, `run_pipeline.py`, or route aggregation unless the generic framework genuinely changed. + +#### `ui/AGENTS.md` + +Expect `ui/AGENTS.md` to orient contributors across the frontend without documenting individual feature internals. + +- It should describe the App Router layout under `ui/src/app/`. +- It should point to reusable feature components under `ui/src/components/`. +- It should mention generated API client usage under `ui/src/client/`. +- It should mention auth readiness constraints under `ui/src/lib/auth/`. +- It should not describe removed folders or outdated stack details. + +### 6. Classify findings + +Use these categories: + +- `stale`: prose mentions files, commands, flows, or architecture that no longer match the repo +- `missing-parent-coverage`: a parent scope omits a major subsystem it should orient the reader to +- `missing-child-agents`: a deep subtree likely needs its own `AGENTS.md` +- `wrong-level`: content belongs in a parent or child scope instead +- `extra-detail`: a parent doc is too implementation-specific for its level + +### 7. Report format + +List findings first, ordered by severity. + +Use this shape: + +```text +: -> -> +``` + +Examples: + +```text +api/AGENTS.md: missing-child-agents -> telephony is a large extension surface with provider registration, transport, routes, and config rules but has no local AGENTS.md -> add api/services/telephony/AGENTS.md and keep api/AGENTS.md at navigation level +api/services/integrations/AGENTS.md: stale -> says central DTO edits are required for new integrations, but registry-based discovery handles node resolution -> update the doc to describe the registry path only +ui/AGENTS.md: wrong-level -> describes individual workflow-builder component behavior instead of frontend navigation rules -> move that detail to a deeper doc or remove it +``` + +After findings, include: + +- open questions or assumptions +- optional patch plan, only if the user asked for fixes or clearly wants them next + +## Editing Rules + +If the user wants the docs fixed: + +- patch the smallest set of `AGENTS.md` files that restores a clean hierarchy +- add a new `AGENTS.md` only when a subtree has distinct local rules or extension contracts +- keep parent docs short and navigational +- let child docs own local implementation rules +- avoid copying the same guidance into parent and child files +- prefer folders over files when writing navigation guidance, unless a file is the only real seam +- when adding a new child `AGENTS.md`, start with the shortest useful contract; avoid tutorial-style prose +- point the reader downward toward the next relevant subdirectory or child `AGENTS.md` +- if a draft feels forced or over-explained, compress it again + +## Useful Commands + +```bash +python .agents/skills/review-agents-md/scripts/inventory_agents_md.py +rg --files -g 'AGENTS.md' . +find api ui -name AGENTS.md | sort +find api/services -maxdepth 2 -type d | sort +find ui/src -maxdepth 2 -type d | sort +rg -n "include_router|all_routers" api/routes/main.py api/services/integrations +rg -n "register\\(|ProviderSpec|register_package|create_runtime_sessions|run_completion" api/services/telephony api/services/integrations +``` diff --git a/.agents/skills/review-agents-md/agents/openai.yaml b/.agents/skills/review-agents-md/agents/openai.yaml new file mode 100644 index 0000000..099cf0a --- /dev/null +++ b/.agents/skills/review-agents-md/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Review AGENTS.md" + short_description: "Audit AGENTS.md scope and drift" + default_prompt: "Use $review-agents-md to audit AGENTS.md files for drift, missing coverage, and wrong scope boundaries." diff --git a/.agents/skills/review-agents-md/references/dograh-seams.md b/.agents/skills/review-agents-md/references/dograh-seams.md new file mode 100644 index 0000000..96390a6 --- /dev/null +++ b/.agents/skills/review-agents-md/references/dograh-seams.md @@ -0,0 +1,126 @@ +# Dograh Seams + +Use this file as a starting map, not as source of truth. Verify every claim against the live repo. + +## Dynamic discovery only + +Do not record the current `AGENTS.md` inventory in this file. + +- discover the live hierarchy with `rg --files -g 'AGENTS.md' .` +- use the helper script to identify uncovered hot spots +- treat any baked-in inventory of existing `AGENTS.md` files as drift-prone and remove it + +## Root-level anchors + +The repo root still revolves around these contributor-relevant directories: + +- `api/` +- `ui/` +- `scripts/` +- `docs/` +- `pipecat/` + +Current code-heavy top-level subtrees that can matter during hierarchy reviews: + +- `evals/` for evaluation tooling, including `evals/visualizer/` +- `sdk/` for packaged SDK work, especially `sdk/python/` and `sdk/typescript/` + +Root `AGENTS.md` should stay at this level. + +## Backend anchors + +### Route aggregation + +- REST routers are aggregated in `api/routes/main.py`. +- Telephony has its main cross-provider route file at `api/routes/telephony.py`. +- Integration package routers are mounted through `api.services.integrations.all_routers()`. +- Node-type metadata is exposed from `api/routes/node_types.py`. + +### Workflow execution + +Workflow execution is not a single folder. + +- workflow graph, DTOs, node data, node-spec generation, QA, and tool helpers live under `api/services/workflow/` +- live pipeline execution lives under `api/services/pipecat/` +- Dograh-specific realtime provider adapters live under `api/services/pipecat/realtime/` +- post-call QA, registered integrations, and webhook execution live in `api/tasks/run_integrations.py` + +If `api/AGENTS.md` implies workflow execution lives in only one place, treat that as suspicious. + +### Node spec and SDK seam + +- core node specs are registered lazily from `api/services/workflow/dto.py` by `api/services/workflow/node_specs/__init__.py` +- integration node specs are merged through `api.services.integrations.all_node_specs()` +- the frontend and SDK-facing node catalog is served from `api/routes/node_types.py` + +### Telephony + +Current telephony architecture is registry-driven. + +- importing `api.services.telephony` eagerly loads `api/services/telephony/providers/` so provider packages self-register +- provider registration and `ProviderSpec` live in `api/services/telephony/registry.py` +- provider lookup, org-scoped config normalization, inbound matching, and run-scoped resolution live in `api/services/telephony/factory.py` +- per-provider HTTP routers live in `api/services/telephony/providers//routes.py` and are auto-mounted by `api/routes/telephony.py` +- provider-local implementations live in `api/services/telephony/providers//` +- current provider packages include `ari`, `cloudonix`, `plivo`, `telnyx`, `twilio`, `vobiz`, and `vonage` +- not every provider has an HTTP route module; for example, `ari` is transport-focused and skipped by the auto-mounter + +### Integrations + +Current integrations are also registry-driven. + +- package discovery lives in `api/services/integrations/loader.py` via `pkgutil.iter_modules(...)` +- package registration and runtime/completion orchestration live in `api/services/integrations/registry.py` +- shared package/session context types live in `api/services/integrations/base.py` +- a concrete package example exists at `api/services/integrations/tuner/` + +## Frontend anchors + +### Navigation and pages + +- page routes live under `ui/src/app/` +- `ui/src/app/layout.tsx` composes the global frontend providers and `AppLayout` +- runtime config handlers live under `ui/src/app/api/config/` +- auth/session handlers live under `ui/src/app/api/auth/` +- feature coverage should be discovered from the current `ui/src/app/` tree, not maintained as a static list here + +### Components and feature slices + +- shared primitives live under `ui/src/components/ui/` +- workflow builder primitives live under `ui/src/components/flow/` +- reusable workflow UI lives under `ui/src/components/workflow/` +- workflow run UI lives under `ui/src/components/workflow-runs/` +- telephony-related UI lives under `ui/src/components/telephony/` +- layout components live under `ui/src/components/layout/` +- workflow feature code is split between reusable components and route-local code under `ui/src/app/workflow/[workflowId]/`, especially `components/`, `contexts/`, `hooks/`, `stores/`, `utils/`, and nested `run/[runId]/` + +### Client and auth + +- generated API client code lives under `ui/src/client/`, with generated subtrees in `ui/src/client/client/` and `ui/src/client/core/` +- auth exports live in `ui/src/lib/auth/index.ts` +- auth provider wrappers live under `ui/src/lib/auth/providers/` +- server-side auth helpers live in `ui/src/lib/auth/server.ts` +- `AuthProvider` chooses between the Stack and local wrappers after fetching `/api/config/auth`, so docs that treat auth as compile-time static are suspicious + +## Known drift example from the audit + +`api/services/telephony/README.md` is still stale in the current repo snapshot: + +- it described flat provider files like `twilio_provider.py` and `vonage_provider.py` +- it told contributors to add schemas to `api/schemas/telephony_config.py` +- it referenced legacy patterns such as direct `TwilioService` usage + +The live code instead uses provider packages under `providers//`, registry-driven provider resolution, and route auto-mounting from `api/routes/telephony.py`. Use this as a reminder that prose in adjacent docs may have drifted even when the code is coherent. + +## Hotspot heuristics + +These are review prompts, not frozen conclusions. + +- pay extra attention to deep subtrees that define extension contracts, registration points, or multi-file execution paths +- in Dograh, common examples include telephony, workflow execution, generated SDK surfaces, and other service subtrees that span many files + +Ask: + +- does the parent doc have enough room to explain this subtree accurately without becoming overloaded? +- does the subtree have distinct extension rules, registration points, or local pitfalls? +- would a contributor benefit from a dedicated `AGENTS.md` here? diff --git a/.agents/skills/review-agents-md/scripts/inventory_agents_md.py b/.agents/skills/review-agents-md/scripts/inventory_agents_md.py new file mode 100644 index 0000000..7f629f8 --- /dev/null +++ b/.agents/skills/review-agents-md/scripts/inventory_agents_md.py @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +"""Inventory AGENTS.md scopes and large uncovered subtrees. + +Run from the repo root: + + python .agents/skills/review-agents-md/scripts/inventory_agents_md.py + +The output is intentionally heuristic. It helps with discovery; it does not +decide by itself whether a subtree must have its own AGENTS.md. +""" + +from __future__ import annotations + +import argparse +import os +from dataclasses import dataclass +from pathlib import Path + +IGNORE_DIRS = { + ".git", + ".hg", + ".svn", + ".next", + ".turbo", + ".venv", + "venv", + "node_modules", + "__pycache__", + ".pytest_cache", + ".ruff_cache", + ".mypy_cache", + ".cursor", + ".claude", + ".idea", + ".vscode", + "dist", + "build", +} + +CODE_EXTENSIONS = { + ".py", + ".ts", + ".tsx", + ".js", + ".jsx", + ".mjs", + ".cjs", +} + + +@dataclass(frozen=True) +class DirSummary: + path: Path + code_files: int + nested_dirs: int + has_agents_here: bool + descendant_agents: int + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Summarize AGENTS.md coverage and likely missing deeper scopes.", + ) + parser.add_argument( + "roots", + nargs="*", + default=["."], + help="Directories to scan. Defaults to the current directory.", + ) + parser.add_argument( + "--hotspot-threshold", + type=int, + default=12, + help="Minimum code-file count for a subtree to be listed as a hotspot.", + ) + return parser.parse_args() + + +def should_skip_dir(name: str) -> bool: + return name in IGNORE_DIRS or name.startswith(".") + + +def walk_dirs(root: Path): + for current_root, dirnames, filenames in os.walk(root): + dirnames[:] = sorted(d for d in dirnames if not should_skip_dir(d)) + yield Path(current_root), dirnames, filenames + + +def discover_agents(roots: list[Path]) -> list[Path]: + found: set[Path] = set() + for root in roots: + for current_root, _dirnames, filenames in walk_dirs(root): + if "AGENTS.md" in filenames: + found.add((current_root / "AGENTS.md").resolve()) + return sorted(found) + + +def count_nested_dirs(root: Path) -> int: + count = 0 + for current_root, dirnames, _filenames in walk_dirs(root): + if Path(current_root) == root: + count += len(dirnames) + continue + count += len(dirnames) + return count + + +def count_code_files(root: Path) -> int: + count = 0 + for _current_root, _dirnames, filenames in walk_dirs(root): + for filename in filenames: + if Path(filename).suffix in CODE_EXTENSIONS: + count += 1 + return count + + +def descendant_agents(root: Path, agents: list[Path]) -> list[Path]: + root_resolved = root.resolve() + return [ + agent + for agent in agents + if agent.parent != root_resolved and root_resolved in agent.parents + ] + + +def immediate_child_dirs(root: Path) -> list[Path]: + children = [] + for child in sorted(root.iterdir()): + if child.is_dir() and not should_skip_dir(child.name): + children.append(child) + return children + + +def summarize_child(child: Path, agents: list[Path]) -> DirSummary: + desc_agents = descendant_agents(child, agents) + return DirSummary( + path=child, + code_files=count_code_files(child), + nested_dirs=count_nested_dirs(child), + has_agents_here=(child / "AGENTS.md").exists(), + descendant_agents=len(desc_agents), + ) + + +def format_path(path: Path, cwd: Path) -> str: + try: + return str(path.resolve().relative_to(cwd)) + except ValueError: + return str(path.resolve()) + + +def nested_hotspots(summary: DirSummary, agents: list[Path], threshold: int) -> list[DirSummary]: + if summary.has_agents_here: + return [] + if summary.code_files < threshold: + return [] + + nested_summaries = [ + summarize_child(child, agents) for child in immediate_child_dirs(summary.path) + ] + return [ + nested + for nested in nested_summaries + if ( + not nested.has_agents_here + and nested.code_files >= threshold + and nested.descendant_agents == 0 + ) + ] + + +def print_scope(scope_dir: Path, agents: list[Path], cwd: Path, threshold: int) -> None: + scope_agent = scope_dir / "AGENTS.md" + child_agents = descendant_agents(scope_dir, agents) + + print(f"AGENTS: {format_path(scope_agent, cwd)}") + + if child_agents: + print(" child AGENTS:") + for agent in child_agents: + print(f" - {format_path(agent, cwd)}") + else: + print(" child AGENTS: none") + + children = immediate_child_dirs(scope_dir) + if not children: + print(" immediate child dirs: none") + print() + return + + print(" immediate child dirs:") + summaries = [summarize_child(child, agents) for child in children] + for summary in summaries: + marker = "has AGENTS" if summary.has_agents_here else "no AGENTS" + extra = "" + if summary.descendant_agents and not summary.has_agents_here: + extra = f", {summary.descendant_agents} deeper AGENTS" + print( + " - " + f"{format_path(summary.path, cwd)}/ -> " + f"{summary.code_files} code files, " + f"{summary.nested_dirs} nested dirs, " + f"{marker}{extra}" + ) + + hotspots = [ + summary + for summary in summaries + if ( + not summary.has_agents_here + and summary.code_files >= threshold + and summary.descendant_agents == 0 + ) + ] + if hotspots: + print(" hotspot children without AGENTS:") + for summary in hotspots: + print( + " - " + f"{format_path(summary.path, cwd)}/ -> " + f"{summary.code_files} code files, {summary.nested_dirs} nested dirs" + ) + else: + print(" hotspot children without AGENTS: none") + + second_level_hotspots = [] + for summary in summaries: + for nested in nested_hotspots(summary, agents, threshold): + second_level_hotspots.append((summary.path, nested)) + + if second_level_hotspots: + print(" nested hotspot children under umbrella dirs:") + for parent, nested in second_level_hotspots: + print( + " - " + f"{format_path(nested.path, cwd)}/ -> " + f"{nested.code_files} code files, {nested.nested_dirs} nested dirs " + f"(under {format_path(parent, cwd)}/)" + ) + else: + print(" nested hotspot children under umbrella dirs: none") + + print() + + +def main() -> int: + args = parse_args() + cwd = Path.cwd().resolve() + roots = [Path(root).resolve() for root in args.roots] + agents = discover_agents(roots) + + if not agents: + print("No AGENTS.md files found.") + return 0 + + print("# AGENTS inventory") + print(f"roots: {', '.join(format_path(root, cwd) for root in roots)}") + print() + + scope_dirs = sorted(agent.parent for agent in agents) + for scope_dir in scope_dirs: + print_scope(scope_dir, agents, cwd, args.hotspot_threshold) + + print("Notes:") + print("- This is a heuristic inventory, not an automatic decision engine.") + print("- A hotspot is only a candidate for a deeper AGENTS.md.") + print("- Always verify architecture claims against live code before reporting drift.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/AGENTS.md b/AGENTS.md index b74bf7a..3147249 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,21 +25,7 @@ dograh/ ## Local Development -### Starting Services - -```bash -# Start infrastructure services (postgres, redis, minio) -./scripts/start_services_dev.sh - -# Stop all services -./scripts/stop_services.sh -``` - -On Windows (PowerShell): -```powershell -.\scripts\start_services_dev.ps1 -.\scripts\stop_services.ps1 -``` +Contributor setup and service startup are documented in `docs/contribution/setup.mdx`. ## Environment Configuration diff --git a/api/AGENTS.md b/api/AGENTS.md index f57e8e8..d1c9d1d 100644 --- a/api/AGENTS.md +++ b/api/AGENTS.md @@ -6,35 +6,40 @@ FastAPI backend for the Dograh voice AI platform. ``` api/ -├── app.py # Application entry point, FastAPI setup ├── routes/ # API endpoint handlers -├── services/ # Business logic and integrations +├── services/ # Domain logic, runtime systems, and extension seams ├── db/ # Database models and data access ├── schemas/ # Pydantic request/response schemas -├── tasks/ # Background jobs (ARQ) -├── utils/ # Utility functions +├── tasks/ # Background jobs and post-call work +├── mcp_server/ # MCP surface exposed by the backend +├── utils/ # Shared utilities ├── alembic/ # Database migrations -├── constants.py # Environment variables and constants └── tests/ # Test suite ``` ## Where to Find Things -| Looking for... | Go to... | -| ---------------------- | ------------------------------------------------------------------------ | -| API endpoints | `routes/` - each file is a router module, aggregated in `routes/main.py` | -| Business logic | `services/` - organized by domain (telephony, workflow, campaign, etc.) | -| Database models | `db/models.py` | -| Database queries | `db/*_client.py` files (repository pattern) | -| Request/response types | `schemas/` | -| Background tasks | `tasks/` - uses ARQ for async job processing | -| Environment config | `constants.py` | +| Looking for... | Go to... | +| ---------------------------- | ----------------------------------------------------------------------------- | +| API endpoints | `routes/` - domain routers mounted under `/api/v1` | +| Workflow graph and node data | `services/workflow/` | +| Live pipeline runtime | `services/pipecat/` | +| Telephony providers/call flow| `services/telephony/` | +| Third-party integrations | `services/integrations/` | +| Campaign and other domains | `services/` | +| Database access | `db/` | +| Request/response types | `schemas/` | +| Background jobs | `tasks/` | +| MCP backend surface | `mcp_server/` | +| Tests | `tests/` | ## API Structure - All routes are mounted at `/api/v1` prefix -- Routes are organized by domain (workflow, telephony, campaign, user, etc.) -- `routes/main.py` aggregates all routers +- Routes are organized by domain under `routes/` +- Workflow execution spans `services/workflow/`, `services/pipecat/`, and `tasks/` +- Telephony is a full subsystem under `services/telephony/`, with provider-specific packages under `services/telephony/providers/` +- Integrations extend through `services/integrations/`; package-specific rules should live in that subtree's own `AGENTS.md` ## Database Migrations diff --git a/api/services/telephony/AGENTS.md b/api/services/telephony/AGENTS.md new file mode 100644 index 0000000..873ac8f --- /dev/null +++ b/api/services/telephony/AGENTS.md @@ -0,0 +1,10 @@ +# Telephony + +Shared telephony code lives here. Provider-specific code lives in `providers/`; +read `providers/AGENTS.md` before changing a provider package. + +- Keep cross-provider contracts, registry/factory wiring, shared status/transfer handling, and org-scoped config resolution in this folder. +- Keep provider-specific transports, serializers, config models, and webhook handlers in `providers/`. +- Resolve providers through the shared telephony helpers in this folder; do not instantiate provider classes directly from routes, tasks, or unrelated services. +- Keep telephony config lookups tenant-safe and respect any run-scoped telephony configuration carried on a workflow run. +- Keep provider-specific HTTP routes in provider packages; shared route glue belongs in `api/routes/`. diff --git a/api/services/telephony/providers/AGENTS.md b/api/services/telephony/providers/AGENTS.md index d8faed6..546b4af 100644 --- a/api/services/telephony/providers/AGENTS.md +++ b/api/services/telephony/providers/AGENTS.md @@ -119,5 +119,5 @@ Pick the closest shape and copy from it. - **Don't add a hardcoded provider list anywhere.** If you need to iterate, use `registry.all_specs()` / `registry.names()`. - **Don't add a route under `routes/telephony.py` for a single provider.** Provider-specific handlers go in `providers//routes.py`. Cross-provider handlers (`/inbound/run`, `/twiml`) stay in `routes/telephony.py`. - **Don't import `.routes` from a provider's `__init__.py`.** That's the cycle we deliberately broke — see "Registration is import-driven." -- **Don't write a frontend form for a new provider.** The UI consumes `GET /api/v1/organizations/telephony-providers/metadata` and renders generically from `ProviderUIField`. If a `field.type` you need doesn't exist (`text`/`password`/`textarea`/`string-array`/`number`), extend the renderer in `ui/src/app/(authenticated)/telephony-configurations/` once — not per provider. +- **Don't write a frontend form for a new provider.** The UI consumes `GET /api/v1/organizations/telephony-providers/metadata` and renders generically from `ProviderUIField`. If a `field.type` you need doesn't exist (`text`/`password`/`textarea`/`string-array`/`number`), extend the shared telephony UI under `ui/src/components/telephony/` once — not per provider. - **Don't run a database migration to add a provider.** The discriminator lives in JSONB credentials and a `VARCHAR(64)` `mode` column; nothing in the DB schema knows the set of provider names.