# Repository Guidelines (Agent-Facing) ## What This Project Is - Guided demo platform built on FastAPI + Playwright to showcase browser automations and GraphQL-backed data flows. - Entry point: `src/guide/app/main.py`; CLI runner: `python -m guide` (respects `HOST`/`PORT`). - Personas and host targets are data-driven via YAML, not hardcoded. - App state wiring happens in `create_app()`: loads settings, builds `PersonaStore`, `ActionRegistry`, and `BrowserClient`, then stashes them on `app.state` for dependency injection. ## Code & Module Layout - Source root: `src/guide/app/` - `actions/` — FastAPI-triggered demo actions; keep them thin, declarative, and side-effect scoped. - `auth/` — pluggable MFA/auth helpers; avoid coupling to specific IdPs. - `browser/` — `BrowserClient` and Playwright wrappers; centralize navigation, timeouts, and tracing. - `core/` — settings, logging, dependency wiring; `AppSettings` drives runtime config and reads YAML/env merges. - `errors/` — `GuideError` hierarchy; routes standardize error responses. - `raindrop/` — GraphQL client + operations; isolate schemas/operations from business logic. - `strings/` — selectors, labels, copy, GraphQL strings; no inline literals in actions. - `models/` — domain/persona models; prefer pydantic v2 models with explicit types. - `utils/` — shared helpers; keep <300 LoC and avoid circular deps. - Config files: `config/hosts.yaml`, `config/personas.yaml` describe targets/personas; never embed secrets. ENV overrides: `RAINDROP_DEMO_BROWSER_HOSTS_JSON` and `RAINDROP_DEMO_PERSONAS_JSON` accept JSON arrays of objects matching the YAML shape. ## Development Workflow - Install deps: `pip install -e .` - Type check: `basedpyright src` - Sanity compile: `python -m compileall src/guide` - Run API (dev): `python -m guide` - Preferred loop: edit → `basedpyright` → `compileall` → manual smoke via CLI or hitting local `/docs`. - API surface (2025-11-22): - `GET /healthz` – liveness. - `GET /actions` – list action metadata. - `POST /actions/{id}/execute` – run an action; yields `ActionEnvelope` with correlation_id. - `GET /config/browser-hosts` – current default + host map. ## Coding Conventions - Python 3.12+, Pydantic v2, pydantic-settings. - Use PEP 604 unions (`str | None`), built-in generics (`list[str]`, `dict[str, JSONValue]`). - Ban `Any` and `# type: ignore`; add type guards instead. - Actions/constants: use `ClassVar[str]` for IDs/descriptions. - Keep modules small (<300 LoC); split by domain (e.g., `actions/intake/basic.py`). - Strings/queries belong in `strings/`; selectors should be reusable and labeled. - `strings.service` enforces domain names; missing keys raise immediately, so keep selectors/texts in sync with UI. ## Error Handling & Logging - Raise `GuideError` subclasses; let routers translate to consistent HTTP responses. - Log via `core/logging` (structured, levelled). Include persona/action ids and host targets in logs for traceability. - For browser flows, prefer Playwright traces over ad-hoc prints; disable tracing only intentionally. - `BrowserClient.open_page` resolves host by ID → CDP attach (reuses existing Raindrop page) or launches headless chromium/firefox/webkit; always close context/browser on exit. - CDP mode requires `host` + `port` in `hosts.yaml`; errors surface as `BrowserConnectionError` with host_id detail. ## Browser Automation Notes - Route all CDP calls through `BrowserClient`; configure timeouts/retries there, not per action. - Keep selectors in `strings/`; avoid brittle text selectors—prefer data-testid / aria labels when available. - When mocking Playwright in tests, isolate side effects and avoid real network/CDP hits. - Current MFA flow uses `DummyMfaCodeProvider` which raises `NotImplementedError`; real runs need a provider implementation or override before executing login/ensure_persona. ## GraphQL & Data Layer - `raindrop/graphql.py` houses the client; operations live in `raindrop/operations`. Keep queries/mutations versioned and colocated with types in `strings/`. - Validate responses with pydantic models; surface schema mismatches as `GuideError`. - Never bake URLs/tokens—pull from `AppSettings` and YAML/env overrides. - HTTP transport via `httpx.AsyncClient` (10s timeout); GraphQL errors bubble as `GraphQLTransportError` or `GraphQLOperationError` with returned `errors` payload in `details`. ## Configuration & Secrets - Runtime configuration flows: env vars → `config/*.yaml` → `AppSettings` defaults. - Do not commit credentials; use env overrides for tokens/hosts. - MFA providers are pluggable; keep provider-specific wiring behind interfaces. - Default env prefix: `RAINDROP_DEMO_` (see `AppSettings.model_config`). ## Testing Expectations - Minimum: `basedpyright src` and `python -m compileall src/guide` before publishing. - Add unit tests under `tests/` alongside code; mock Playwright/GraphQL; avoid real external calls. - Prefer deterministic fixtures; document any required env vars in test module docstrings. - Action pipeline happy-path: request → resolve persona + browser host → `BrowserClient.open_page` → optional `ensure_persona` → action.run → `ActionEnvelope` with correlation_id from `ActionContext`. ## Git & PR Hygiene - Commits: scoped and descriptive (e.g., `feat: add auth login action`, `chore: tighten typing`). - PRs should state changes, commands run, and any config additions (`hosts.yaml`, `personas.yaml`). - Link related issues; include screenshots/log snippets when UI or API behavior changes. ## Performance & Footprint - Keep browser sessions short-lived; close contexts to avoid leaking handles. - Cache expensive GraphQL lookups when safe; prefer per-request timeouts over global sleeps. - Avoid widening dependencies; stick to project pins unless justified. - Close Playwright contexts/browser handles promptly (client already wraps in contextmanager; keep action code lean to avoid dangling pages). ## Quick Start Checklist (new agents) - [ ] Create/verify `config/hosts.yaml` and `config/personas.yaml` match your target env. - [ ] Run `pip install -e .` - [ ] Run `basedpyright src` and `python -m compileall src/guide`. - [ ] Start API: `python -m guide` and open `/docs` to smoke basic actions. - [ ] Review `strings/` for selectors relevant to your feature before writing new actions. - [ ] If executing auth flows, supply a working `MfaCodeProvider` (current dummy raises) or stub the login call in demos/tests.